deallocを明示的に書くこと

ARCの素晴らしい所はretain/releaseをコンパイラとランタイムが面倒を見てくれるお陰で、プログラマの負担が大幅に減ることだが、だからといってオブジェクトの参照と解放に対して無神経になって良い訳ではない。

以前にも書いたがARCは自らretain/releaseを書くことが許されていないので、思わぬ所で意図せずオブジェクトが確保/解放されている場合がありこれがバグを非常に解り難くしている。※

いつオブジェクトが解放されているかを知るためにはdeallocが動作していることを確認すれば良いわけで、ここから、まだ半年にも満たないObjective-Cプログラマ経験から

デバッグで困ったらdeallocを明示的に書くべし」

という経験則が適用できる。
deallocを書くのは簡単だ。全てのクラスでdeallocメソッドをオーバライドできる。

- (void)dealloc
{
    //後始末処理
}

ただしARC下では[super dealloc]は呼べないので注意が必要。

後処理が必要の無い場合であってもなんらかの処理を書いておけばdeallocが呼ばれることを確認することができるので、どのタイミングでreleaseされているかの確認ができる。

私の場合、配列クラスに追加したはずのオブジェクトがテストをしてみると全く存在していなかったケースがあった。

id arry = [[NSMutableArray alloc] init];

//諸々処理中で配列にaddObject
HogeObject* o = [[HogeObject alloc] init];
[arry addObject:o]
  :
  :100件追加する
  :
int count = [arry count];
STAssertEquals(count, 100, nil); × 結果はなぜか0

最初は全く原因が解らなかったのだが、HogeObjectクラスにdeallocメソッドを追加してスタックトレースを調べて見た所、

arry = [fooObject stringProperty];

と、fooObjectのプロパティ(NSString*型)のプロパティでarryを上書きしていたのである。
arrayはそれまでセットされていた配列はARCによってセット後に解放されるが、解放される際にはコレクション中に所持しているオブジェクトもこれまたARCで全て解放されていた訳だ。

まあこれは私が間抜けなだけだけが、deallocを敢えて書かなかったならばもう少し発見が遅れていたことだろう。

※その逆に循環参照で解放されていないケースがあるが、これはdeallocとは関係が無いので割愛する。