2012年8月29日水曜日

deallocでnil代入すべきではない理由について

XCodeが生成するコードを見ると、viewDidUnloadではnil代入によってプロパティ初期化しているのに、deallocではインスタンス変数を直接releaseしています。

なぜ二つのやり方を使い分けるのか、疑問に思ったことがないですか?

---

オブジェクトの解放は、すべてself.hoge = nilでやるべきだと推奨する人もいます。

なぜかというと、プロパティの実体であるインスタンス変数をreleaseしたあとの、self.hogeが指し示すアドレスは、nil(0番地)ではなくもともとデータが存在していたアドレスです。

そこにアクセスしようとすると、retain忘れや過剰releaseと同様に、忌まわしいEXC_BAD_ACCESSが発生します。

nil代入はオブジェクトを解放しつつ、プロパティのポインタを安全なnilで初期化する良いイディオムなのです。

---

しかしキー値監視が行われている可能性がある場合、話は変わります。

キー値監視とは、その名前の通り、他のオブジェクトのプロパティが変化したとき、コールバックを受け取る仕組みのことです。もしプロパティにキー値監視が行われている場合、nil代入によって通知が行われるかもしれません。

しかしdeallocフェーズではオブジェクトの状態は不安定になり、メソッドの結果は予測不可能になります。

運がよければ何事もなく正しい結果を返します。悪ければ必要なメンバが既に解放されているため、不正なアドレスからデータを読み込もうとしてエラーで落ちます。

ものすごく運が悪いと、メソッドの実行に必要なメンバが解放されているにも関わらず、処理自体は通って不正な値を返すといったことがありえます。後々になってそのことが発覚するような場合、デバッグは困難を極めます。

deallocフェーズでのプロパティ操作は、オブジェクトが不安定な状態でキー値監視通知を発生させる恐れがあるので、XCodeが生成するコードではそれを避けるようになっているのです。

---

キー値監視なんて使ったことないし関係ないじゃん?みたいに思うのですが、CocoaやInterfaceBuilderが裏側で使っているとか使っていないとかみたいです。(iOSでの依存度については、良く分かんないんですが…)

0 件のコメント:

コメントを投稿