Developer's Blog

クラッシュの原因にありがちなイージーミス

アプリがクラッシュする原因はさまざまです。その中でも、解放(dealloc)済みオブジェクトへのアクセス (EXC_BAD_ACCESS) によるクラッシュの原因としてありがちなイージーミスを2つ挙げてみます。

インスタンス変数に autorelease なオブジェクトをそのまま代入

インスタンス変数は通常そのクラスのインスタンスがオーナーシップを持つ必要があります。この場合の典型的な書き方として次の2つが挙げられます。

  • retain で宣言されているプロパティ経由でオブジェクトを代入する場合(self.button = aButton;)
  • インスタンス変数に代入するときに retain 等 retainCount を増やすメッセージを送っておく場合(button = [aButton retain];)

この2つの書き方が混在していると次のような間違いが起こりがちです。

UIButton *aButton = [UIBtton buttonWithType:UIButtonTypeRoundedRect];
button = aButton;

この場合、aButton 変数には autorelease が呼ばれた状態なのでラン・ループが回った時に dealloc されてしまいます。その後 button 変数にアクセスをすると EXC_BAD_ACCESS を起こしてクラッシュしてしまいます。

このようなミスをしないためにも、インスタンス変数にオブジェクトを代入するときには、代入しようとしているオブジェクトのオーナーシップがどうあるべきか気に掛けましょう。

非同期で動作するオブジェクトのデリゲート

非同期で動作するオブジェクト(UIWebView や NSURLConnection など)のデリゲートでも注意が必要です。たとえば、UINavigationController 内で次のような構成になっているアプリがあるとします。

  • RootViewController: UINavigationController の RootViewController
  • DetailViewController: RootViewController から Push される UIWebView を持つ View Controller であり UIWebView のデリゲート

RootViewController から横遷移して DetailViewController の UIWebView が Web ページをロード中に RootViewController に戻ると落ちる場合があります。これは非同期で動作している UIWebView の delegate にセットされていたオブジェクトが dealloc 済みだった場合に起こります。

この問題を回避するには UIWebView をリリースする前に delegate に nil を代入してやります。UIWebView の リファレンスにも次のような注意書きがあります。

Important: Before releasing an instance of UIWebView for which you have set a delegate, you must first set its delegate property to nil. This can be done, for example, in your dealloc method.

UIWebView の例のように非同期で動作するオブジェクトのデリゲートになっているオブジェクトは、自身が dealloc される前に delegate を nil にするのが無難です。

今回あげたような解放済みオブジェクトへのアクセス (EXC_BAD_ACCESS) によるクラッシュを極力さけるため、私は普段から release を呼んだ後にその参照に必ず nil を代入するよう心がけています。

Copyright © 2019 Fenrir Inc. All rights reserved.