[cocos2d-x]setTouchEnabledしたらEXC_BAD_ACCESSエラーで進まない(解決)

Written by uechoco 3月 02
この記事を読む時間:551くらい

cocos2d-xでシングルタッチで遊べるミニゲームを作っているんですが、表題のエラーでだいぶ時間を食いました。cocos2d-x 2.2.2です。

GameSceneというCCLayerを継承したクラスを作ってそいつを実行していました。Spriteを追加して表示して、scheduleUpdate()で無操作で移動するところまではすんなり行きました。ところが、init()内でsetTouchEnabled()して実行したらCCObject::retain()でEXC_BAD_ACESSというエラーが発生しました。

  1. void CCObject::retain(void)
  2. {
  3.     CCAssert(m_uReference > 0, "reference count should greater than 0");
  4.  
  5.     ++m_uReference;
  6. }

ブレークポイントを設定して見てみると、m_uReferenceの値が表示されない。最初はCCAssertに引っかかったと思ってリファレンスカウンタがなんで0以下の値で呼ばれたんだろうと考えていたんだけれども、だいぶ時間を浪費してから thisにあたるCCObject* が 0x0 になっているのに気がついた。NULLオブジェクトに対してretain()しようとしていることが判明した。実行ツリー上ではCCTouchHandler::initWithDelegate()から呼ばれているので今度は呼び出し元を見てみることに。

  1. bool CCTouchHandler::initWithDelegate(CCTouchDelegate *pDelegate, int nPriority)
  2. {
  3.     CCAssert(pDelegate != NULL, "touch delegate should not be null");
  4.  
  5.     m_pDelegate = pDelegate;
  6.  
  7.     dynamic_cast<CCObject*>(pDelegate)->retain();
  8.  
  9.     m_nPriority = nPriority;
  10.     m_nEnabledSelectors = 0;
  11.  
  12.     return true;
  13. }

dynamic_castからのretain()というところ。pDelegateはGameSceneのインスタンスが渡ってきているのは確認できた。ブレークポイントを該当行に設定してlldbデバッガを使って状況を確認する。(参考サイト:Xcode4.5でLLDBデバッガコマンドを使ってみる – Object for cutie

  1. (lldb) p pDelegate
  2. (cocos2d::CCTouchDelegate *) $0 = 0x095484a4
  3. (lldb) p dynamic_cast<CCObject*>(pDelegate)
  4. Internal error [IRForTarget]: Couldn't rewrite one of the arguments of a function call.
  5. error: The expression could not be prepared to run in the target
  6. (lldb) p ((CCObject*)pDelegate)->m_uReference
  7. (unsigned int) $1 = 1
  8. (lldb) p ((CCObject*)pDelegate)->retain()
  9. (lldb) p ((CCObject*)pDelegate)->m_uReference
  10. (unsigned int) $2 = 2

はーなるほど。C言語の感覚でキャストしてみたらretain()できたので、dynamic_castが超絶怪しいことまでわかった。とわいえ最新版でこの程度のところでバグが残っているはずがないので、自分のクラス定義を疑う。

  1. class GameScene: CCLayer
  2. {
  3. protected:
  4.   // 省略
  5. };

書籍のコードと読み比べると、CCLayerにpublicがついていない。それだけかと思ってとりあえずpublicを補った。

  1. public class GameScene: CCLayer
  2. {
  3. protected:
  4.   // 省略
  5. };

動いた!!!意味分かんない。dynamic_castってなんやねん。ぜんぜんダイナミックに変換してくれないじゃん。そもそもこのpublicってなんやねん。

勉強:

継承修飾子というものらしい。省略時はprivate指定とみなされるらしい。「privateな継承では、サブクラスのインスタンスを、スーパークラスの型としては扱えない」とか書いてある。えーちゃんとCCTouchDelegateで渡ってきてるじゃん。よくわかんない。。。

C++力が足りないんだな。

## ところで1年もブログ記事書いてなかったのか。。。しかも最後の記事はcocos2dだけど1年ぶりに書くのはcocos2d-xとか。