今日の「こけめも」。

(さしたる意味はないが、「コケメモ」を平仮名にしてみた)


EXC_BAD_ACCESSエラー

現象
ある操作をすると、EXC_BAD_ACCESS で落ちることが判明。

同じ操作をしても、iPhoneシミュレータでは EXC_BAD_ACCESS 出ないが、実機(iPod touch)だと必ず落ちる。
(その後、「ある操作」「必ず」というのは間違っていたことが分かる。)


怪しい箇所
なんとなく、怪しそうなところ。
NavigationController周り。

[self.navigationController popViewControllerAnimated:YES];

self.navigationController.delegate = self;

UINavigationControllerDelegate



参考記事:デリゲート関連が怪しそう
木天蓼(マタタビ)の記録: EXC_BAD_ACCESSなエラーの原因

UIPopoverのデリゲートをselfに設定して呼び出し、自身がNavigation Controllerによって(多分)リリースされた後にデリゲートが呼ばれてクラッシュしてた。

デリゲートの役目は安易に設定すると痛い目に会うぞという教訓でした。


ふむ。


以下のページに、「非同期で動作するオブジェクトのデリゲート」についての記載。

クラッシュの原因にありがちなイージーミス (フェンリル | デベロッパーズブログ)

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


ふむ。
ということで、EXC_BAD_ACCESSエラー対策案。
delegate を nil にしてみようと(delegateの明示的なnil)。


viewDidLoad で delegate のインスタンスを設定しているので、viewDidUnload で nil に設定すればいいのかな、と思って、念のためネットを調べる。

以下、参考。

[iPhone] UIViewController の dealloc と viewDidUnload - それはBlog

どうやら、viewDidLoad と viewDidUnload は対ではない模様。


しかし、delegate を nil を試してみたが、EXC_BAD_ACCESSエラーは発生。

何度も試しているうちに、タイミングも実は同じタイミングじゃないことがわかった。


参考記事:reloadData
Objective-C - popToRootViewControllerAnimated:NOでEXC_BAD_ACCESSの事例 - Qiita [キータ]

親のビューコントローラに戻す前に、reloadDataでセルの再描画を行なっていた(選択されたものにチェックマークをつける目的だった)。reloadDataにより呼ばれたtableView:cellForRowAtIndexPath:内で参照していたメンバ変数が、popTo…によって既に破棄されていたで落ちているっぽい。


しかし、今回は popToRootViewControllerAnimated:YES にしている。


勉強になる記事
iOS 開発で、EXC_BAD_ACCESS とさよならするための6つのルール | Zero4Racer PRO Developer's Blog

これは面白い記事。

そもそもEXC_BAD_ACCESSとは何か?

EXC_BAD_ACCESSというエラーは、あるメモリにアクセスしようとしたが、そのメモリ領域にあったデータは既に解放されていて、何も見つかりませんでしたという、致命的なエラー


そもそもARCについて理解が足りてないのが根本的な問題のような気がしてきた。


Appleのドキュメントを見ると、deallocで、self.something=nilを行うのは、KVO[キー値監視通知]が発生する危険があるため、勧められていません。viewDidUnloadで行えば問題ありません。(setter、init、deallo以外では)しかし、viewDidUnloadは、必ず呼ばれる訳ではないので、deallocで、releaseしてあげましょう。


ARCでは、weakプロパティを使用することによって、上記の作業を自動でコンパイラに行わせることが出来ます。


うむ。。


あと、FMDBのライブラリ周りのメモリ管理も少し気になる。

キーワード、allocで検索。
(後々考えると、このあたりは関係無かった)


対策と結果
触っていると、データ一覧画面からホームへ戻って、選択画面へ遷移しようとするだけでも発生。
データ一覧画面はNavigationControllerDelegateを利用。


NavigationControllerDelegateの利用をやめてみてどうなるか、試してみよう。


EXC_BAD_ACCESSが発生しなくなった。

対策は、NavigationControllerDelegateを使わなくすることで出来た。

元々、NavigationControllerDelegateを使う必要が無かった。

tableView reloadDataを昨日追加することで、データ一覧の再描画の対策はできていた。




余談
ソースコードが汚くなってきた。
リファクタリングしよう・・・。



以上♨