読者です 読者をやめる 読者になる 読者になる

オブジェクトの生成とメモリ管理

iPhone開発一般 Objective-C

Objective-C2.0では、以下の2つのメモリ管理方式が、ランタイムシステムによって提供されています。

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つ多くなってしまうため、それもメモリリークの原因となる。

メソッドの引数として渡す場合

呼出し先が、引数として渡したオブジェクトをどのように扱うのかによって、対処が異なる。そのメソッドのリファレンスマニュアルなどをよく読み、状況を理解することが重要である。
自分はオーナーシップを放棄し、参照を持つ必要がないのであれば、ほとんどの場合、そのメソッドから復帰した直後にreleaseすることができる。

メソッドの戻り値として返す場合

インスタンス変数に保持しないものは、autoreleaseされた状態で返すようにする。


以上、ざっくりとまとめてみましたが、いかがでしょうか?
プログラミングに関しては、その時々で、いろいろな条件が複雑に重なるので、これらがすっぽりあてはまらないことも多々あります。例えば、マルチスレッド環境の場合は、また別に考えるべきことがあるでしょう。
この内容が、何かのヒントになれば幸いです。