iPhoneで開発してて、よく突き当たるのがEXC_BAD_ACCESS。
大抵は多重releaseしてしまうなどの単純ミスですが、稀に不可解なEXC_BAD_ACCESSに陥ることがありますよね。今回たまたま不可解なEXC_BAD_ACCESSが解決できたので、その顛末を参考までに書いてみます。
今回起きたのは「UIWebViewで画像長押し時にActionSheetを表示する」という処理の中。
UIWebViewは内包する子Viewの中で処理を行い、UIWebView自体は入れ物でしかなく、UIWebViewにUILongPressGestureRecognizerを登録しても無意味です。 なので「UIWebViewで画像長押し」を検知するのに色々小技が必要になります。
今回検知のためにNSObjectのrespondsToSelector:をmethod swizzlingしていました。ここで長押しを検知し、NSNotificationCenterを経由してActionSheetを出す、という仕組み。処理としては、
- (NSObject) respondsToSelector:
NSNotificationCenterのpostNotificationName:object:を使って自作クラスのハンドラを呼び出す。 - (自作クラス) notificationハンドラ
ここでUIActionSheetを構築、表示 - (UIActionSheet) showInView:
という流れ。
この処理の流れでテストを行っていたところ、うまく動作するときもあるのですが、(3)の段階でEXC_BAD_ACCESSが起きることがあります。(2)でUIActionSheetを構築してる段階では問題なし。UIActionSheetの構築コードは元々バッチリ動いていた箇所からの転用なので、それが原因とも思えないですし、UIActionSheetを作ってもshowInView:を呼び出さなければEXC_BAD_ACCESSも起きないため構築に問題があるわけではないらしい。
当然UIActionSheet関連処理をすべてコメント化するとEXC_BAD_ACCESSは起きない。
正直かなりお手上げ状態。 なにがダメなのか???
上記処理は1から3まですべて同期的に動くので、それがダメなのかな?
試しに(3)を別メソッドに切り出し、(2)からperformSelector:withObject:afterDelay:を使って非同期式に変えてみたところEXC_BAD_ACCESSが出なくなりました!わーい!
というわけで、私が今回ハマったEXC_BAD_ACCESSではrun loopに戻る前にやってはいけないことをやっているようでした。たぶんrespondsToSelector:から戻った後の処理のなにかとバッティングしているのでしょう。なにとバッティングしているのかは全くわからず、Appleのみぞ知ることでしょう。回避できたのはたまたまです。
不可解なEXC_BAD_ACCESSがいつも同じ理由というわけではないですが、こういうこともあるようです。困ったときの参考になると幸いです。