現在時刻を表示する

タイマーを用いて定期的に実行する

定期的にメソッドを実行するにはNSTimerクラスを利用する。

NSTimer *timer;

まずヘッダファイルでタイマーを定義する。タイマー処理はscheduledTimerWithTimeIntervalを用いる。

timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(tick:) userInfo:nil repeats:YES];

引数は、①呼び出し間隔(秒)②タイマー発火時に呼び出すメソッドのあるオブジェクト③タイマー発火時に呼び出すメソッド④タイマー発火時に呼び出すメソッドに渡す情報⑤繰り返しをするか否かの5項目。@selectorは、メソッド名を記述することでそのメソッドを呼び出すことができる。Objective-Cでは、Selectorを用いることで実行時に動的に呼び出すメソッドを変更できるとのこと。

- (void)viewDidUnload {
	[timer invalidate];
	timer = nil;
}
- (void)dealloc {
	if ( timer)
	[timer invalidate];
	[super dealloc];
}

なおタイマーを停止する際は、invalidateメソッドを使用する。invalidateはreleaseまでしてくれる。

現在時刻を表示する

最後に定期的に呼び出すメソッド内に現在の時刻を取得し、必要な形に整形する処理を記述する。

- (void)tick:(NSTimer *)theTimer {
	// 初期化(init)し現在の時刻を取得する
	NSDate *date = [NSDate date];
	// NSDateFomatter=NSDateの値を整形する
	NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
	[formatter setLocale:[NSLocale currentLocale]];
	// 年月日曜日を全て表示する
	[formatter setDateStyle:NSDateFormatterFullStyle];
	// 時分秒タイムゾーンを全て表示する
	[formatter setTimeStyle:NSDateFormatterFullStyle];
	// Date型からString型に置換する
	NSString *dateStr = [formatter stringFromDate:date];
	// allocしたformatterをreleaseする
	// なおClassObjectである*dateは自動的にメモリ解放されるので記述は不要
	[formatter release];
}

引数のtheTimerは、呼び出し元のタイマー自身を示す。

Objective-Cのメモリ管理

リファレンスカウンタ

各インスタンスを他のいくつのオブジェクトが参照しているかを示すカウンタ。参照元のオブジェクトがretainというメッセージを送ることからretain counterとも呼ばれる。alloc/initされたとき、もしくはretainが1つ送られると値が1つカウントアップし、releaseメッセージを送ることで1つカウントダウンされる。カウンタ値が0になった時点でインスタンスは解放される。

retainメッセージ

  • 参照する際はretainを必ず送る。
  • 送らなければ他のオブジェクトがreleaseメッセージを送ったときに意図せずカウンタ値が0となり、インスタンスが解放されてしまうことがある。

releaseメッセージ

  • 使用後は必ずreleaseメッセージを送る。
  • releaseメッセージは参照元のdeallocメソッドに記述する。
  • リファレンスカウンタが0になるとdeallocメソッドが実行される。
// リファレンスカウンタが0になると呼び出される
- (void)dealloc {
	// releseメッセージを送り、カウンタを1つ減らす
	[obj release];
 
	[super dealloc];
}

ViewControllerなどはviewDidUnloadedにもreleaseを記述する。viewDidUnloadedはメモリ不足の際に実行される。nilを記述するのは、viewDidUnloadedのあとにdeallocが呼び出されたときにアクセスエラーになるのを防ぐため。

- (void)viewDidUnload {
	// Release any retained subviews of the main view.
	// e.g. self.myOutlet = nil;
	[label release];
	label = nil;
}

Auto Reference Counting(ARC)

コンパイラが自動で上記のようなretain, releaseを挿入してくれる機能がARC(Auto Reference Counting)である。deallocも適切な位置に挿入してくれる。ARCによりretain, releaseなどを自分で記述する必要がなくなり、書き忘れや誤記によるメモリーリーク等の障害回避が期待される。ARCはiOS 5.0 SDKに付随するLLVM3.0より利用可能である。

オーナーと強参照、弱参照

ARCにおいて、オブジェクトの参照方法は強参照と弱参照の2種類がある。二つの参照方法の違いは、参照元のオブジェクトが被参照オブジェクトのオーナーとなるか否かである。強参照の場合、参照元オブジェクトが被参照オブジェクトのオーナーとなり、オーナーのいる間この被参照オブジェクトはメモリ上に生存している。オーナーが一人もいなくなったオブジェクトは破棄される。

//"str"が"hoge"を強参照しておりオーナーである
NSString *str = [[NSString alloc] initWithFormat@"hoge"];
//"str"が強参照を止めたため、"hoge"は解放される
str = nil;

強参照の場合、参照を止めるまでメモリ上に被参照オブジェクトが存在し続ける。このためViewControllerでviewDidUnloadedされた場合は、明示的にオブジェクトの参照を止める必要がある。

- (void)viewDidUnload {
	self.str = nil;
}

二つのオブジェクトが互いに強参照し合い(循環参照)、オブジェクトが破棄出来なくなることを防ぐためには弱参照を使用する。また、既に間接的に強参照しているオブジェクトへさらに直接参照する場合にも弱参照を使用する。例えばInterfaceBuilderでViewを作成している場合、ViewControllerはトップのViewを強参照し、トップのViewは各サブViewを強参照しているので、IBOutletを作成しViewControllerから各サブViewをダイレクトに参照する場合は、弱参照で構わない。

@propety(weak, nonatmic) IBOutlet UIView *view;

一方で、ViewControllerが間接的に強参照していないViewに関してはIBOutletで強参照しておかないと、自分の予期せぬところでオブジェクトが破棄されてしまうことがある。

@propertyとアクセサ

クラス内のインスタンス変数をIB(や他のクラス)で利用する場合、IBより値が参照できるようにアクセサ(putter, getter)を各クラスで実装しておく必要がある。しかし@propertyを記述することで、実際のアクセサを実装しなくても値の参照が可能となる。

#import <UIKit/UIKit.h>
 
@interface DigitalClock1ViewController : UIViewController {
	// インスタンス変数の宣言
	UILabel *label;
}
@property(weak, nonatmic) IBOutlet UILabel *label;
@property(strong, nonatmic) NSString *str;
@end

@propertyのあとの属性はいくつでも指定できる。

属性 挙動
nonatomic スレッドセーフじゃないけど動作は早い?
weak 弱参照。参照先のObjectは、強参照しているObjectが無くなると消滅する
strong 強参照。強参照している間は、対象先のObjectは消滅しない

Storyboad上のObjectはViewController自身が間接的に強参照している、したがってIBOutletで再度ダイレクトに強参照する必要は無い。ただしViewControllerが管轄していないObjectに関しては、Storyboad上にあったとしても強参照しなければならない場合がある。Storyboad以外のObjectを参照する場合も同様に強参照すべきかどうか検討する必要がある。

@synthesize label;

最後に@implementationに@synthesizeを記入することで、getメソッドとputメソッドが自動的に実装される

self.str = nil;
[self setLabel:nil];

viewUnloadedメソッドで各Objectの解放を行う