オートリリースプールとスレッドとNSOperation

Cocoaのリファレンスカウント式のメモリ管理で利用されるオートリリースプールはメインスレッドに対しては自動的に作られて自動的に解放されるので普段はあまり意識することはないのだけど、performSelectorInBackground:withObject:などを利用して別スレッドで処理を行う時には自前でオートリリースプールを作成しないとメモリリークが発生してしまいます。

以下は自前オートリリースプール作成のサンプルコード。doItメソッドは別スレッドで処理が行われるため、メソッド内部でオートリリースプールを自前で立てています。

- (void)doIt {
	// オートリリースプールを作成
	NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
	NSString *s = [[[NSString alloc] initWithFormat:@"hoge"] autorelease];
	NSLog(@"%@", s);
	// オートリリースプールとプールにたまったオブジェクトをリリース
	[pool release];
}

- (IBAction)doButtonDidPress {
	[self performSelectorInBackground:@selector(doIt) withObject:nil];
}

doItメソッドの中のNSAutoreleaseを生成する部分を取り除くと下記のようにコンソールに「オートリリースプールがないよー」と警告メッセージが表示されるので実際にオートリリースプールが効いているのが確認できる。

*** __NSAutoreleaseNoPool(): Object 0x5b3a440 of class NSCFString autoreleased with no pool in place - just leaking

(おまけ)オートリリースプールとNSOperation

別スレッドを立ててバックグラウンドで処理を行うもうひとつの方法としてNSOperationを利用する方法もある。NSOperationを利用する時には、なぜだか分からないのだけど、オートリリースブールを立てる必要なないみたい。NSOperationQueueかNSOperationの親クラスのあたりでヨロシクやってくれてるのかもねー。

- (void)doIt {
	// オートリリースプールがなくても警告メッセージが出ない
	NSString *s = [[[NSString alloc] initWithFormat:@"hoge"] autorelease];
	NSLog(@"%@", s);
}

- (IBAction)doButtonDidPress {
	NSOperationQueue *q = [[NSOperationQueue alloc] init];
	NSOperation *op = [[NSInvocationOperation alloc] initWithTarget:self 
														   selector:@selector(doIt) 
															 object:nil];
	[q addOperation:op];
	[op release];
	[q release];
}