ストップウォッチを作ろう(2) - Cocoaプログラミング

いよいよ実装です。お決まりの-initや-deallocも説明します。

てくっち

まずは前回作ったヘッダファイルをもう一度。

てくっち

#import <Foundation/Foundation.h>

@interface Stopwatch : NSObject {
    NSTimeInterval offsetTime;
    NSDate *startTime;
    BOOL isBusy;
}
@property(readonly) NSTimeInterval second;
@property BOOL isBusy;
- (void)startStop;
- (void)reset;
@end

てくっち

前回からちょこっと変わってますが、これをもとにしてすすめますー。

てくっち

ちょこっとどころか、たくさん変わってますね汁

てくっち

インスタンス変数は3つあって、

てくっち

NSTimeInterval型(実はdouble)のoffsetTime、NSDateのstartTime、BOOLのisBusyです。

てくっち

てくっち

1回計測するだけならstartTimeとSTOPしたときの時刻の差を計算するだけでいいですが、

てくっち

その後リセットせずにSTARTした場合、START直前の値を覚えておくことにします。これがoffsetTime。

てくっち

そうすると2回目以降の計測は、startTimeからの経過時間に、offsetTimeを足した値になります。

てくっち

このしくみにたどり着くまで、半日かかりましたね!orz

てくっち

つまり1つの計測値のために、内部では2つの値を保持することになります。

てくっち

あと、isBusyはYESまたはNOの値をとるタイプで、

てくっち

START後にYES、STOP後にNOになるようにしましょう。「計測中で忙しいワン」ということです。

てくっち

次に

てくっち

@property(readonly) NSTimeInterval second;
@property BOOL isBusy;

てくっち

プロパティが2つ。

てくっち

前回はsecondはインスタンス変数にもありましたが、今回はstartTimeとoffsetTimeから計算することにします。

てくっち

外から見ると、secondが内部にあるのかどうなのか、見えないわけですね。というか、知る必要もないわけです。

てくっち

- (void)startStop;
- (void)reset;

てくっち

そしてメソッドがこの2つ。

てくっち

ということで、Stopwatch.mに実装してきますー。まずはプロパティについて。

てくっち

isBusyはお決まりの@synthesizeで、

てくっち

@synthesize isBusy;

てくっち

こう書くだけで、isBusyを読み出すメソッドと書き込むメソッドを作ってくれるのでした。

てくっち

- (BOOL)isBusy
{
        return isBusy;
}

- (void)setBusy:(BOOL)yn
{
        isBusy = yn;
}

てくっち

つまりこれの代わり。

てくっち

プロパティsecondについては独自に計算する必要があるので、自前で定義します。

てくっち

- (NSTimeInterval)second
{  
  NSTimeInterval second;
  second = -[startTime timeIntervalSinceNow];
  second += offsetTime;
  return second;
}

てくっち

NSDate型のインスタンスであるstartTimeに対して、timeIntervalSinceNowを送っていますねー。

てくっち

こうすると、startTimeが表す時刻と、現時刻の差を返してくれます。

てくっち

この値は常にマイナスになっちゃうので、符号を反転しております。そしてoffsetTimeを足す。

てくっち

このように、ヘッダファイルで@propertyを用いて宣言していても、独自の動作をさせたいときは自前で定義できます。

てくっち

次に-startStopメソッドの実装ですー。

てくっち

- (void)startStop
{
  if (isBusy) {
    // STOP
    offsetTime = self.second;
    [startTime release];
    startTime = nil;
    self.isBusy = NO;
  }
  else {
    // START
    startTime = [[NSDate alloc] init];
    self.isBusy = YES;
  }  
}

てくっち

これがこのクラスの心臓部なんですが…

てくっち

それぞれのインスタンス変数ごとに見ていってください。

てくっち

startTimeは、STARTで新しく作り出し、STOPで解放。

てくっち

offsetTimeは、STOPしたときにsecondの値を入れる。

てくっち

isBusyは、それぞれYES/NOを反転。

てくっち

・・・ってな具合です。

てくっち

そして-resetメソッドは、

てくっち

- (void)reset
{
  self.isBusy = NO;
  offsetTime = 0.0;
}

てくっち

以上2つのメソッドの中で、isBusyの値をセットするのに、わざわざ自身のアクセサメソッドを呼び出していますけど

てくっち

なぜこんな回りくどいことをするかというと、後でちょっと楽できるしかけを利用するためですー。

てくっち

そして最後に、あと2つ、メソッドを書く必要があります。

てくっち

ヘッダで宣言もしてないのにどうして!と思いますよね。

てくっち

- (id)init
{
  [super init];
  [self reset];
  return self;
}

- (void)dealloc
{
  [startTime release];
  [super dealloc];
}

てくっち

この2つです。initとdealloc

てくっち

initは自分自身が生成されたときに最初に呼ばれるメソッド。

てくっち

deallocは自分が解放されて消滅する直前に呼ばれるメソッドです。

てくっち

こいつらは親クラスであるNSObjectで定義されているので、Stopwatchクラスのヘッダでもう1回宣言する必要はなくて、

てくっち

でも動作を変えたいときは、こうやって実装部に書くんです。

てくっち

どういうふうに動作を変えたいかというと、まずinitでは、インスタンス変数の初期値の設定ですね。

てくっち

ここではresetメソッドを使い回しています。

てくっち

deallocでは、インスタンス変数のうちオブジェクトを、忘れずに解放してあげねばなりません。

てくっち

つまりNSDate型のstartTimeですね。

てくっち

オブジェクトでない単なる数値については、何もせずに放置していいです。

てくっち

ここでオブジェクトの解放を忘れると、Stopwatchのインスタンスが解放されたのに、そいつが使ってたstartTimeインスタンスがメモリ上に残されて

てくっち

誰からも使われないままになってしまいます。

てくっち

メモリリークというやつですね。

てくっち

インスタンス変数のうち、オブジェクトは、忘れずに解放しましょう。

てくっち

次回は、いよいよプロジェクトを作ってストップウォッチを動かしてみましょう!

和田

わーい!わーい!