オブジェクトの生成とメモリ管理
Objective-C2.0では、以下の2つのメモリ管理方式が、ランタイムシステムによって提供されています。
- マネージドメモリ
- プログラマが、オブジェクトを保持するのか、解放するのかを明示的に指定する。あるオブジェクトが、誰かに保持されているかどうかは、参照カウンタによって管理される。その参照カウンタが0にならないと解放されない。詳細は、Advanced Memory Management Programming Guideを参照。
- ガベージコレクション
- システムが、自動的に、どこからも参照されていないオブジェクトを解放する。詳細は、Garbage Collection Programming Guide (Not Recommended): Garbage Collection Programming Guideを参照。
iPhoneOSのランタイムシステムの場合、前者の「マネージドメモリ」方式なので、オブジェクトの参照がどうなっているのかを気にしながらプログラミングする必要があります。このときに最も注意しなければならないのは、オブジェクトのオーナーシップを意識するということです。
すべてのケースを挙げきれるものではないですが、ここでは、オブジェクトを生成した場合のいくつかのパターンを例に、オーナーシップをどのように捉えればいいのかについて、説明してみたいと思います。
ローカル変数で保持する場合
+allocメソッドを使って生成したオブジェクトには、参照カウンタに1が設定(retain)されるので、その瞬間に生成元がオーナーになる。ブロック内のローカル変数で参照する場合には、そのままにしておくと、そのブロックを出たときに参照は切れるが、参照カウンタは1なので、メモリリークの原因となる。以下のいずれかの処置が必要である。
- 生成と同時に、autoreleaseしておく。それによって、解放を予約し、オーナーシップを放棄したような状態になる。その後、AutoreleasePoolによって自動的に解放される。便利であるが、大量のオブジェクトを生成する場合は、AutoReleasePoolにもオブジェクトが大量に溜まり、実際の解放処理でパフォーマンスが悪くなることがあるので注意が必要。
NSMutableArray *anArray = [[[NSMutableArray alloc] init] autorelease];
- そのオブジェクトを使い終わった後の、適切なタイミングでreleaseする。それによって参照カウンタが1減算される。そこで0になった場合にはその場で解放される。オブジェクトの生存期間が短くなるので、効率がよい。
{ NSMutableArray *anArray = [[NSMutableArray alloc] init]; // (必要な処理) // ... [anArray release]; }
ファクトリメソッドが提供されているクラスの場合は、それを使って生成することもできる。その場合は、一般的にautoreleaseされたオブジェクトが返される。(注: 例外もあるので、各メソッドのリファレンスマニュアルで確認する必要がある)
NSMutableArray *anArray = [NSMutableArray array];
この例では、ファクトリメソッドは呼出し元の依頼でオブジェクトを生成するだけであり、オブジェクトを保持しないので、呼出し元がオーナーとなる。
インスタンス変数で保持する場合
ほとんどの場合、自分がオーナーとして責任を持つ必要があるので、retainしなければならない。その参照は、-deallocメソッドが呼び出された時に、必ずreleaseされるようにしなければならない。
インスタンス変数への代入時には、それまでその変数で参照を持っていたオブジェクトを忘れずにreleaseする必要がある。
プロパティを使ってアクセスすることで、そういったメモリ管理に関するプログラミングの手間は、かなり低減される。プロパティに、retain属性を指定することで、代入時に必要なretain/releaseを暗黙的に実行してくれる。
@property (nonatomic, retain) NSMutableArray *products; ... @synthesize products; ... // それまでに保持されていたオブジェクトはreleaseされ、 // 新しく設定されるオブジェクトがretainされる。 self.products = [NSMutableArray array];
注: すでにretainされたオブジェクトをここに設定すると、参照カウントが1つ多くなってしまうため、それもメモリリークの原因となる。