11月 162011
 

iOS 5では数々の機能が追加されましたが、その中でも開発者の私たちにとって嬉しかったのはARC(Automatic Reference Counting)ではないでしょうか。そこで、ARCの概要から注意点まで、基本的なところを何回かに分けてまとめていきたいと思います。

ARCとは?

ARC (Automatic Reference Counting) とは、その名の通り、自動リファレンスカウンタ。リファレンスカウンタ方式のメモリ管理を自動で(正確にはコンパイラが)行ってくれるというものです。

ご存知リファレンスカウンタ方式のメモリ管理では、retain, releaseなどのメソッドを用いて生成したオブジェクトの保持状態を管理しています。ARCを使うことで、これらの管理に必要なコードをコンパイラ(LLVM 3.0)が自動で挿入してくれるという、なんとも便利なものなのです。

しかしながら、美しいものには刺があるわけで、ARCも使い方に注意しないと痛い目に合いそうです。しっかりと理解してから使いましょう。

retain, release, autorelease, deallocはコンパイラのお仕事

ARCを利用する場合、コンパイラが

  • retain, release, autoreleaseを挿入してくれる(自分で呼んではいけない。コンパイラエラーになる)。
  • deallocを適切な位置に挿入してくれる(deallocのオーバーライドは可能。ただし[super dealloc]は不可能)。

ことになります。

つまり、ARCを有効にすることで、これまで気にしていたrelease忘れによるメモリリークや、retain忘れによるクラッシュなどを防げるようになるのです。

ただし、ARCはガーベージコレクションとは違います。あくまでも、コンパイラが一定のルールに則って適切な場所に適切なコードを入れてくれるものです。したがって、その「ルール」をしっかり把握しておく必要があるのです。

オブジェクトの生存状況

これまでは、自分が保持しておきたいオブジェクトはretainしていました。ARCではこれは不要(不可能)です。ARCにおけるオブジェクト利用の考え方の基本は、

  • オーナーのいる(強参照されている)オブジェクトは常に利用可能
  • オーナーがいなくなったら(スコープ外に出た場合も含む)破棄される

です。ここで、オーナーとは、あるオブジェクトを強参照している変数のことを指します。

なお、「参照」には「強い参照 (strong reference)」と「弱い参照 (weak reference)」があるので注意が必要です。オーナーになれるのは、「強い参照」をしているときのみです。

以下で、例にあげて考えてみます。

強い参照 (Strong reference)

(s1)
まず、firstName変数(修飾子を伴わないものはすべてstrong reference)に”natsu”というテキストを入れてオブジェクトを生成します。この時点で、firstNameはNSString型オブジェクト@”natsu”を強参照しています。言い換えると、firstNameは@”natsu”のオーナーであると言えます。

(s2)
次に、同じく強参照のaName変数にfirstNameを代入しました。@”natsu”は、firstNameに加え、aNameからも参照されるようになりました。この時点で、@”natsu”のオーナーはfirstNameとaNameです。

(s3)
次に、firstNameの内容を変更します。新たなオブジェクトを生成し、”maki”というテキストをセットしたとしましょう。

ここからfirstNameは、新たに生成されたオブジェクト@”maki”を参照します。つまり、@”natsu”のオーナーではなく、@”maki”のオーナーになりました。これで、@”natsu”のオーナーは、aNameだけとなりましたが、オーナーが残っているのですから、@”natsu”オブジェクトはメモリ上に存在しています。

(s4)
新しい変数otherNameにfirstNameを代入しました。@”maki”のオーナーが増えています(強参照が増えている)。

(s5)
aNameにotherNameを代入します。この時点で、aNameは@”maki”を参照するようになり、@”maki”のオーナーになります。@”natsu”にはオーナーがいなくなりました。したがって、このオブジェクトはここで破棄されます。

ARC outline strong reference


弱い参照 (Weak reference)

次に、弱い参照 (Weak reference) の場合を見てみます(弱い参照を示す場合、__weak修飾子を使います。詳細は後述)。

(w1)
強い参照のときと同様に、firstNameを初期化します。firstNameは@”natsu”のオーナーです。

(w2)
弱い参照であるweakName変数にfirstNameを代入しました。weakNameは@”natsu”を参照していますが、この参照は「弱い参照」です。つまり、weakNameは@”natsu”のオーナーではありません。

(w3)
firstNameが変更されました。そのため、firstNameは@”natsu”のオーナーではなく、新たに生成された@”maki”のオーナーとなりました。

この時点で、@”natsu”は誰からも強参照されなくなりました。すなわち、オーナーがいなくなったのです。そのためこのオブジェクトはここで破棄されます。

このとき、weakNameにはnilが代入されます。これは、Zeroingといって弱い参照の特徴です。破棄済みオブジェクトのアドレスが残らないので、不要なクラッシュを避けることができます。

ARC outline weak


所有修飾子

参照には強い参照と弱い参照があることは上述したとおりです。それぞれの変数がどのような参照をするべきかは、修飾子で指示します。

__strong

まず、一番よく使う__strong修飾子です。デフォルトが__strongとなっているため、何も書かずに変数宣言をした場合、すべて強い参照となります。

強い参照をしている限り、その変数が参照しているオブジェクトはメモリ上にしっかりと生きています。

また、__strongをはじめ、後述する__weak, __autoreleasing変数はすべてnilで初期化されます。

__weak

弱い参照を示します。例えば、delegateパターンを使うときは、弱い参照を使わないと相互循環(Strong reference cycle)してしまいメモリリークが発生します。

弱い参照の特徴は、参照先のオブジェクトが誰からも強参照されなくなったとき、自動的にnilが代入されるということです(Zeroing)。

ひとつ注意点として、弱い参照はiOS 4とMac OS X v10.6では利用できません。これらのOSバージョンがDeployment Targetに設定されている状態で__weak修飾子を使うと、コンパイラエラー (The current deployment target does not support automated __weak references) となります。そのようなときは、次の__unsafe_unretainedを使いましょう。

__unsafe_unretained

参照形態は__weakと同等ですが、こちらはnil初期化やZeroingが行われません。つまり、参照先のオブジェクトが破棄されたあともアドレスが残っているので使用の際には注意が必要です。

__autoreleasing

最後に__autoreleasingです。これは、これまでオブジェクトにautoreleaseメッセージを送信していたような働きをします。しかし、強参照されているオブジェクトも、変数スコープ外に出れば破棄されるため、あえて明示的に__autoreleasing修飾子を使うことはまれだと思われます。

また、__autoreleaseは、メソッドの引数として(id*)等を渡すときにも利用されます。この場合、返された(id*)型のオブジェクトはautoreleaseされています。例えば、
-(BOOL)performOperationWithError:(NSError * __autoreleasing *)error;
のようなメソッドは、(NSError **)型の引数を渡す際に__autoreleasingが使われています。

その他の注意点

AppleのTransitioning to ARC Release Notesによると、ARCを利用する際には以下のルールを守る必要があります。

  • retain, release, retainCount, autoreleaseをコールしてはいけない。また、これらのメソッドを実装してもいけない。
  • deallocをコールしてはいけない(オブジェクトの解放以外の処理が必要な場合、deallocを実装することは可能。ただし、[super dealloc]はコールしない)。
  • NSAllocateObject, NSDeallocateObjectは使えない。
  • C構造体の中でオブジェクトポインタを使うことができない(代わりにObjective-Cのクラスを生成すべき)。
  • idとvoid *間でのキャストには特定の方法を利用する必要がある。
  • NSAutoReleasePoolは使えない。ただし、@autoreleasepoolブロックの使用が可能。
  • メモリゾーンは使えない。
  • “new”から始まるプロパティ名は使えない。

キャストとautoreleaseの話は別途まとめる予定です。それ以外は明白だと思います。

最後の”new”から始まるプロパティ名に関しては、そのような名前のプロパティを宣言するとコンパイラエラーになります。エラーの内容が、”Property’s synthesized getter follows Cocoa naming convention for returning ‘owned’ objects”と、ちょっと分かりにくいのでご注意を。

まとめ

ARCの解説第一弾として基本概念をまとめました。引き続き、使用時の注意点やTipsなどをまとめていく予定です。

使い方よりも何よりも、まずは基本概念を知っておくことが重要です。循環参照によるリークなどを引き起こさないよう、ARCによるメモリ管理とは何かをしっかりと理解しておきましょう。

質問、間違いの指摘などは、ツイッターでお願いします。 @natsun_happy

参考資料

ARCの詳細を書きました↓

iOS5プログラミングブック
加藤 寛人 吉田 悠一 藤川 宏之 西方 夏子 関川 雄介 高丘 知央
インプレスジャパン
売り上げランキング: 243,952

iOS 6 UICollectionViewについて書きました↓

iPhoneアプリ開発エキスパートガイド iOS 6対応
加藤 寛人 藤川 宏之 高丘 知央 西方 夏子 吉田 悠一 関川 雄介
インプレスジャパン
売り上げランキング: 4,856