Effective Objective-C 2.0の中に、ZombieObjectの事が書いてあり面白かったので、自分なりにまとめてみました。
ZombieObjectの話の前に、解放されたオブジェクトにアクセスするとどうなるのか?の話をします。
解放されたオブジェクトにアクセスするとどうなるのか?
使い終わり解放されたオブジェクトにアクセスすると、普通はクラッシュします。ARCの登場以前は、呼吸をするようにクラッシュしてましたが、ARC以後はその頻度も減りました。でもまだクラッシュするときはします。ニンゲンダモノ。シカタガナイネ。
このとき、「偶然にもクラッシュしない」場合が発生します。これは、オブジェクトを解放後、そのアドレスでアクセスした場合に、ゴミデータにアクセスできてしまったり、別オブジェクトがそこに割り当てられていたりなど、いろいろな原因があり得ます。
↑の例では、_assignString変数のアドレスに、偶然NSLogで使った“assignString=“という文字列が割り当てられて、クラッシュせずに動作してしまったんじゃないかな、と思います。
ZombieObjectを有効にすると、解放したオブジェクトにアクセスすると止まるようになる
「時々クラッシュする」というのは非常にやっかいです。デバッグしても原因の特定が難しく、憶測でいろいろソースを修正してしまいがちです。そしてだいたい憶測は外れます。結果わけがわからない状態になったりもします。
ZombieObjectを有効にすると、このような「解放されたオブジェクトへのアクセス」があると、そこで100%止まるようになります。
ZombieObjectを有効にするには
プロジェクトのSchemeを開き、”Diagnostics”で”Enable Zombie Objects”にチェックを入れます。
この状態でデバッグ実行し、解放されたオブジェクトにアクセスすると、”message sent to deallocated instance 〜”といったログを出して止まるようになります。
ZombieObjectは何をやっているのか
ここからは自分の推測も入っているため、間違っている可能性があります。
通常、オブジェクトをallocした場合、mallocにてメモリが確保されます。そしてretainCountが0となり解放された場合、freeでメモリが解放されます。この時メモリ上のデータはゴミとして残ります。このアドレスにアクセスした場合、もしかしたらクラッシュせずに動作するかもしれません。また、新しいオブジェクトが同じアドレスに生成(malloc)された場合、古いオブジェクトを参照していたはずのアドレスが残っていると、新しいオブジェクトへの参照として動作してしまう可能性があります。
ZombieObjectが有効な場合、”オブジェクトを解放してもメモリを解放(free)せずに、ゾンビとして残す”ような挙動をします。
オブジェクトが解放される時、元々のオブジェクトのデータを残しゾンビ化します。次にオブジェクトがallocされても、ゾンビ化したオブジェクトのメモリには上書きせず、別領域をmallocします。またゾンビ化したオブジェクトは、どんなメソッドが呼ばれても例外を出すようになります。
この仕組みで、「解放されたオブジェクトにアクセスが来ると止まる」ようになります。
一方、この仕組みではメモリが解放されないので、どんどんメモリが肥大化するはずです。なので、いつもは無効にしておいて、時々クラッシュするような厄介な状態になったら有効にする、といった使い方が良いと思います。
そういえばZombieって、ほかの場所で見た事あるような、、
InstrumentsでProfileするときに、”Zombies”ってありましたね。これは解放されたオブジェクトにアクセスした場合に停止して、「そのオブジェクトが何処でalloc/retain/releaseされたのか」を追っかける事が出来るので便利です。