アクセサ通さないとretainされない件
self.hogeと書かないとアクセッサ通さない→retainされない(これは知らなかった)
上記のようなブコメがあったので、
実際のコードで検証したいと思います。
#import <Cocoa/Cocoa.h> @interface Foo : NSObject { NSArray *hoge_; } @property (nonatomic,retain) NSArray *hoge_; -(id)initWithHogeNoRetain:(NSArray *)hoge; -(id)initWithHoge:(NSArray *)hoge; @end @implementation Foo @synthesize hoge_; -(id)initWithHogeDirect:(NSArray *)hoge{ if(self = [super init]){ hoge_ = hoge; } return self; } -(id)initWithHoge:(NSArray *)hoge{ if(self = [super init]){ self.hoge_ = hoge; } return self; } -(void)dealloc{ [hoge_ release]; [super dealloc]; } @end
このオブジェクトのプロパティhoge_のretainCountを出力してみます。
メモリ管理を自分でやる為、NSArrayはalloc+init系で生成しています。
int main(int argc,char **argv){ NSAutoreleasePool *p = [[NSAutoreleasePool alloc] init]; NSArray *array1 = [[NSArray alloc] initWithObjects:@"a",@"b",@"c",nil]; Foo *h1_1 = [[Foo alloc] initWithHoge:array1]; NSLog(@"retainCount: %d",[array1 retainCount]); Foo *h1_2 = [[Foo alloc] initWithHoge:array1]; NSLog(@"retainCount: %d",[array1 retainCount]); Foo *h1_3 = [[Foo alloc] initWithHoge:array1]; NSLog(@"retainCount: %d",[array1 retainCount]); [array1 release]; NSLog(@"retainCount: %d",[array1 retainCount]); [h1_1 release]; NSLog(@"retainCount: %d",[array1 retainCount]); [h1_2 release]; NSLog(@"retainCount: %d",[array1 retainCount]); [h1_3 release]; // ↓ここをコメントアウトはずせばEXC_BAD_ACCESS // NSLog(@"retainCount: %d",[array1 retainCount]); [p release]; return 0; }
なお、retainCountが0の状態を出力しようとすると既にオブジェクトが解放されてしまった後なのでコマンドライン実行ではSegmentationFaultが発生して終了します。gdbで実行してみるとEXC_BAD_ACCESSが発生しているのがわかります。
解放後のオブジェクトにはアクセスしないようにしましょう。
以下、実行結果です。retainCountが増減しているのがわかります。
2010-02-05 13:07:16.209 retainTest[64470:903] retainCount: 2 2010-02-05 13:07:16.212 retainTest[64470:903] retainCount: 3 2010-02-05 13:07:16.212 retainTest[64470:903] retainCount: 4 2010-02-05 13:07:16.213 retainTest[64470:903] retainCount: 3 2010-02-05 13:07:16.213 retainTest[64470:903] retainCount: 2 2010-02-05 13:07:16.214 retainTest[64470:903] retainCount: 1
次は、アクセサを通さない場合です。
int main(int argc,char **argv){ NSAutoreleasePool *p = [[NSAutoreleasePool alloc] init]; NSArray *array2 = [[NSArray alloc] initWithObjects:@"a",@"b",@"c",nil]; Foo *h2_1 = [[Foo alloc] initWithHogeDirect:array2]; NSLog(@"retainCount: %d",[array2 retainCount]); Foo *h2_2 = [[Foo alloc] initWithHogeDirect:array2]; NSLog(@"retainCount: %d",[array2 retainCount]); Foo *h2_3 = [[Foo alloc] initWithHogeDirect:array2]; NSLog(@"retainCount: %d",[array2 retainCount]); [array2 release]; NSLog(@"retainCount: %d",[array2 retainCount]); [h2_1 release]; NSLog(@"retainCount: %d",[array2 retainCount]); [h2_2 release]; NSLog(@"retainCount: %d",[array2 retainCount]); [h2_3 release]; // NSLog(@"retainCount: %d",[array2 retainCount]); [p release]; return 0; }
以下実行結果です。retainCountが増加していません。
最後まで出力されずにSegmentation faultが発生してしまいます。
2010-02-05 13:17:59.857 retainTest[64584:903] retainCount: 1 2010-02-05 13:17:59.860 retainTest[64584:903] retainCount: 1 2010-02-05 13:17:59.861 retainTest[64584:903] retainCount: 1 2010-02-05 13:17:59.861 retainTest[64584:903] retainCount: -1 Segmentation fault
以上の検証結果から、プロパティ宣言時にretainを指定していたとしてもアクセサを通さずにへ設定した場合はretainされないことが判ります。
重要視されていないのか、入門書にもあまり記載されていないと思います。
EXC_BAD_ACCESSが発生したら、アクセサを通していない処理があるのではないかと疑ってみてください。
[追記]
↓Googleのコーディングガイド読んでて気づきました。こちらも参照ください。
Googleスタイルガイドのプロパティ定義でアクセサを強制 - fn7の日記