ページ

2009年10月31日土曜日

NSZombieEnabled

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク

EXC_BAD_ACCESS の話題が Stack Overflow で上がっていた。

Break on EXC_BAD_ACCESS in XCode? - Stack Overflow

メモリ解放済みのインスタンスへアクセスする時にこのエラーが発生することが多い。デバッグの方法として NSZombieEnabled が紹介されていた。これを使うと解放済みのインスタンスへアクセスすると EXC_BAD_ACCESS の代わりに例外が発生し、エラー原因が発見しやすくなる。

CocoaDev の解説:
CocoaDev: NSZombieEnabled


サンプルプログラムを作って試してみよう。

サンプル:NSZombieEnabledStudy.zip


コードは簡単で、インスタンス変数 _data を初期化後すぐに release し、後でボタンが押された時にこれを参照する。

@interface AppController : NSObject {
NSData* _data;
}
-(IBAction)click:(id)sender;
@end


@implementation AppController

- (void)awakeFromNib
{
    if(getenv("NSZombieEnabled") ||
        getenv("NSAutoreleaseFreedObjectCheckEnabled")) {
            NSLog(@"NSZombieEnabled/NSAutoreleaseFreedObjectCheckEnabled enabled!");
    }

    _data = [[NSData allocinit];
    [_data release];
}

-(IBAction)click:(id)sender
{
    NSLog(@"_data=%@", [_data description]);
}


ボタンを押すと、解放済みの NSData を使おうとするのでプログラムはクラッシュして EXC_BAD_ACCESS を出す(デバッグ実行の場合)。


実行中...
プログラムはシグナルを受信しました:“EXC_BAD_ACCESS”。





続いて NSZombieEnabled を有効にしてみよう。

NSZombieEnabled を有効にする方法はアプリ起動時の引数に与える方法と gdbの設定ファイル(~/.gdbinit)に指定する方法があるようだ。

(参考)

今回は前者の方法を試してみた。Xcode で実行可能ファイルの「情報を見る」を開く。



「環境に設定される変数」へ 名前 "NSZombieEnabled"、値 "YES" として指定する。


デバッグ実行してみる。


NSZombieEnabledStudy[15646:813] NSZombieEnabled/NSAutoreleaseFreedObjectCheckEnabled enabled!
NSZombieEnabledStudy[15646:813] *** -[NSConcreteData description]: message sent to deallocated instance 0x148e90
(gdb)


EXE_BAD_ACCESS の代わりに問題となった箇所が表示されるようになった。これはいい。

※なお、通常の実行(Cmd+R)ではメッセージは出ない。デバッグ実行(Cmd+Y)する。