いよいよ実装です。お決まりの-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インスタンスがメモリ上に残されて
誰からも使われないままになってしまいます。
メモリリークというやつですね。
インスタンス変数のうち、オブジェクトは、忘れずに解放しましょう。
次回は、いよいよプロジェクトを作ってストップウォッチを動かしてみましょう!
わーい!わーい!