2010年4月13日火曜日

Objective-Cのautoreleaseの使いどころ

Objective-Cのautoreleaseに関する個人的なメモです。


最終回 メモリ管理を理解する(後編)@IT
http://www.atmarkit.co.jp/fcoding/articles/objc/08/objc08a.html

@ITのObjective-C入門連載記事からautoreleaseに関連する部分を抜粋します。


メソッドの内部で生成した(その時点でretainされた)オブジェクトをクラス側では保持せず(つまりインスタンス変数にセットせず)、メソッドの戻り値として返す場合があります。

生成時に発生した所有権についてはメソッドの責任範囲にありますが、メソッド内ですぐ解放してしまっては、戻り値を受け取った側で利用できなくなってしまいます。

しかしながら、deallocで解放しようとしてもインスタンス変数で保持していないので、アクセスすることができません。このような場合には、autoreleaseメソッドによって、のちのち自動解放されるように予約しておきます。
このパターンはけっこう多そうなのでメモ。


(NSStringクラスの)initWithCString:encoding:は、allocしたあとに呼び出すことでオブジェクトを初期化します。この方法でオブジェクトを生成した場合、releaseのタイミングは自分で考慮する必要があります。

一方、stringWithCString:encoding:は、alloc→init→autorelease まで手続きを済ませたオブジェクトを返してくれます。すでにNSAutoreleasePoolが用意されている文脈内でこのメソッドを利用してオブジェクトを生成すれば、のちのオブジェクト解放について意識する必要はなくなります。

Objective-Cの多くのクラスでは、上記のように単に初期化を行うメソッドと、alloc→init→autorelease を一気に行ってオブジェクトを生成してくれるメソッドの2パターンが用意されています。

これらはメソッドのネーミングルールが統一されており、初期化メソッドは「initWith~」、インスタンス生成メソッドは「xxxWith~(xxxには "string" や "array" など、オブジェクトの汎用的な名称が入ります)」のようになっていますので、慣れればすぐに使い分けられるようになるでしょう。
この違いはあまり意識していませんでした。
単純に「initWith~」の場合だけreleaseメソッドを使っていればいいかと思っていたのですが、秘密はautoreleaseにあったようです。



Google Objective-Cスタイルガイド 日本語訳
http://www.textdrop.net/google-styleguide-ja/objcguide.xml

こちらはGoogleのObjective-Cのコーディング規約です。
初学者には特に参考になるドキュメントですが、その中からautoreleaseに関する部分を抜粋します。


生成時に autorelease するのが望ましい
一時的なオブジェクトを新しく生成するときには、そのメソッドの後ろの方で個別に release するのではなく、オブジェクトの生成と同じ行で autorelease すること。
ほんの少し遅くなるが、うっかり release を消してしまったり release の前に return を入れてしまって、メモリリークが発生するのを防くことができる。
// AVOID (unless you have a compelling performance reason)
MyController* controller = [[MyController alloc] init];
// ... code here that might return ...
[controller release];
// BETTER
MyController* controller = [[[MyController alloc] init] autorelease];
なるほど、確かにautoreleaseを利用すると安全性が高まりそうです。
ただ、AppleのiPhoneアプリのサンプルコードだとほとんどこの(BETTER)形式は見られません。
autoreleaseのコスト的な問題でしょうか。


autorelease してから retain する
オブジェクトの代入は、autorelease してから retain するというパターンに従うこと。
新しいオブジェクトを変数に代入するときには、メモリリークを起こさないように最初に古いオブジェクトを解放する必要がある。これには「正しい」やり方がいくつかある。私たちは「autorelease してから retain する」という方法を選んだ。間違いにくいためだ。タイトなループでは、autorelease pool がいっぱいになり、効率が少し悪くなることに注意しよう。それでも私たちはこのトレードオフは許容できると考えた。
- (void)setFoo:(GMFoo *)aFoo {
  [foo_ autorelease];  // Won't dealloc if |foo_| == |aFoo|
  foo_ = [aFoo retain]; 
}
@propertyディレクティゔを利用しないで自分でセッターメソッドを書くときに使えそうです。




autoreleaseを使うと本当に遅くなる?という疑問を実測しながら検証中です

releaseとautoreleaseのベンチマーク比較記事です。
パフォーマンス的にはそれほど差はない!?

0 件のコメント: