1年半ぐらい前にiOS4時代でXCode3.2で時が止まっていたので、良い機会なので勉強したメモを書くでぇ。
リファレンスカウンタって何?
iOS5.0以降からみんな大好きARCになったわけだが、リファレンスカウンタ方式の理解が無いのはあかんで。
JavaのGCに慣れてしまうとなかなか面食らいますねぇ。でも理屈は単純なんだよね。オブジェクトがどれだけ他から参照されているかをカウントして0になったらメモリ領域から消えていく。
で、その参照カウンタとして計上されるパターンは大きく2つ。alloc initして自分でオブジェクトを生成するか、retainするかのどっちか。copyした場合は別だよな・・・やべぇ確認しよう。
Foo *foo = [[Foo alloc]init]; Foo *foo2 = [foo retain];
で、仮にFooクラスにhogeっていうプロパティがあった場合、[foo2 release]って書いてもfoo2.hogeはリリースされない。メモリ空間が別物という扱いになるから。deallocを宣言してちゃんと解放していれば別だけど。これに最初困った。releaseしたらnullになるからぬるぽになるんじゃねって思ったけど、どうも違うらしい。そうやって空気を読むのはARCとGCだけっぽい。
ARCが有効になっていない環境では、こういうコードはメモリリークを引き起こす可能性が高い。
//Hoge.h @interface Hoge : NSObject @property(nonatomic,retain) NSString *name; @end //Hoge.m #import "Hoge.h" @implementation Hoge @synthesize name = _name; -(id)init { if (self == [super init]) { return self; } return nil; } @end
で、こんな感じで使うとする。
//main Hoge *hoge = [[Hoge alloc] init]; hoge.name = @"hoge"; NSLog(@"%@",hoge.name); [hoge release]; //解放されてないから! NSLog(@"%@",hoge.name);
コメントにあるように、hoge.nameは解放されなかった。もちろんHogeのdeallocで[_name release]を追加すれば解放されますが。
また、retainってのはシャローコピーなので誰かがretainしたオブジェクトに変更を加えるとretainして取得したオブジェクトも変更される。
releaseにはautoreleaseってのもある。これってメソッド抜けたぐらいで速攻releaseされるぐらいの感覚でよさげ。そのタイミングでautoreleaseが走ってる可能性が。確かめてないけど感覚的にそんな感じ。
オーナーシップがよくわかるコードは、配列にオブジェクトを入れるコードが一番説明がしやすい。
NSMutableArray *arr = [[NSMutableArray alloc] initWithObjects:[Hoge alloc init],[Foo alloc init],nil];
これだとまずい。HogeとFooのオーナシップが二つ存在することになるから。
ひとつは配列arr。もう1つは、このコードを書いているクラスそのもの。どっかでこのarrをリリースすれば配列の要素に対してはNSMutableArrayがちゃんとreleaseしてくれるけど、このクラスが保有するHogeとFooに対するオーナーシップが解放されないし、さっきも書いたけどこのクラスのインスタンス自体をreleaseしても自分でalloc initしたものはリリースされないのでメモリに残り続ける。
なので、autoreleaseしてやるか明示的にreleaseしてやる。
//autoreleaseする場合 NSMutableArray *arr = [[NSMutableArray alloc] initWithObjects: [[[Hoge alloc] init] autorelease],[[[Foo alloc] init] autorelease],nil; //自分でreleaseする場合 Hoge *hoge = [[Hoge alloc] init]; Foo *foo = [[Foo alloc] init]; NSMutableArray *arr = [[NSMutableArray alloc] initWithObjects:hoge,foo,nil]; [hoge release]; [foo release];
これでおk。
ただループの中でallocして何かをする場合はループの中でreleaseしてあげたほうが絶対早い。