iPhoneアプリ作成までの道のり4

4. メモリアロケーションのルールを構築する

ここでは、GCやARCを使わないことを前提に話をする。
Objective-Cにおけるメモリ管理については、このサイトが非常に参考になる。まず覚えておかなければならないことは、

・initしたものは必ずreleaseすること
・initWith〜も同様、必ずreleaseすること
・arrayWith〜のように、initのつかない名前のfactoryメソッドを使って生成した場合はreleaseしてはならない
・オブジェクト参照を属性に保持する場合はretainすること
・retainで属性に保持した参照は、そのオブジェクトを失う時、あるいはdealloc内でreleaseすること

これらが基本事項である。
自分で作る初期化メソッドについても、同様の命名規則を適用することが重要である。
尚、Objective-Cでは、nilオブジェクトに対するメッセージ送信は「何も起きない」のであって「エラーにはならない」ため、nilオブジェクトに対してreleaseを呼び出すのは安全である。ただし、「nilではない、既に解放されたオブジェクト」に対してメッセージを送信した場合はBAD_ACCESSになる。

次に重要なのが、AutoreleasePoolである。これについても、上記の参考サイトに詳しく説明がある。要は、オブジェクトを捨てるためのバケツを用意しておいて、イベントループが回る度にバケツを空にするという考え方である。iPhoneアプリの場合は、AutoreleasePoolは既に用意されていて、適切なタイミングで破棄されると考えて良い。この巨大バケツアーキテクチャを使えば、ほとんどすべての場合において、属性に保持する以外のローカルオブジェクトへの参照は、initと同時にautoreleaseしてしまって良いということになる。initで始まらない、arrayWith〜のような生成メソッドは、内部で自動的にautoreleaseされているので、自分でreleaseする必要はない(してはならない)、ということである。

上記のリンクを張ったサイトの解説にもあるが、オブジェクトのオーナーシップを意識することは非常に重要である。例えば、あるクラスのプロパティにオブジェクトへの参照を保持するとしよう。この時、getterにはretainやreleaseを付けるべきか? setterにはretainやreleaseを付けるべきか? オブジェクトのオーナーシップに従うならば、答えは、前者はNO, 後者はYESである。

・自分が作成したオブジェクトは自分で破棄する
・自分が保持しているオブジェクトは自分で破棄する
・他人から受け取ったオブジェクトを自分が保持する場合はretainする

これらを守るだけで良いのだ。
getterに関して言えば、「自分が持っているオブジェクトを他人に渡す」のであるからして、getterからオブジェクトを返す側は何もしない。getterの呼び出し側が、そのオブジェクトを保持するならば、retainするのだ。ある関数内でgetして使うだけならば、retainする必要はない。もしretainしたのであれば、責任を持って自分で解放しなければならない。setterに関して言えば、「他人から渡されたオブジェクトを保持する」のであるからして、明示的なretainが必要である。更に、setされる前にnilでないオブジェクトを所持していたのであれば、それは自分の所有物であるからして、新しいオブジェクトをsetする前に、元のオブジェクトをreleaseしなければならない。こういった動作は、実際に、@synthesizeした場合に自動的に行われている挙動である。