獅子さんのアプリ開発日記
cocos2d-x / XCode によるスマートフォンのアプリ開発(c++)
Monday, September 23, 2013
cocos2d-xの autorelease() 関数について
先日、けっこう困ったことがあったのでメモ。cocos2d-x のオブジェクトを継承したクラスのメモリ管理関連。 <div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgFvBv86lG57C3sYhxGLPb7SMX1j0AqzYHieHbSaKlSrDkjPXuZlurnMNFg0CPONwVFCOUDCx6504e_VhJF_VsmQ_8j7C6I0o1uMAbA4p3-DSLl25tsyd5htzrV_84lxXsMUol6njK0nyk/s1600/game1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgFvBv86lG57C3sYhxGLPb7SMX1j0AqzYHieHbSaKlSrDkjPXuZlurnMNFg0CPONwVFCOUDCx6504e_VhJF_VsmQ_8j7C6I0o1uMAbA4p3-DSLl25tsyd5htzrV_84lxXsMUol6njK0nyk/s320/game1.png" /></a></div> CCLabelTTFを内包する、「メッセージボックス管理クラス」を作成してみた。(ゲーム上に出るメッセージボックスを表示したりするクラス。上の画像では「ステータス」「戦闘メッセージ」の2つです) cocos2d-x の作法に習って CCObjectクラスを継承し、CCSprite等を参考に実装。(ソースは説明のため実際より簡略化してある) <pre class="prettyprint"> // DQ風ウィンドウマネージャ "SSMsgBoxDQ.h" class SSMsgBoxDQ : public CCObject { public: SSMsgBoxDQ(void); virtual ~SSMsgBoxDQ(void); static SSMsgBoxDQ* createWithArgs(kSSMsgBoxType type, float fontSize, int cntCols, int cntRows, int cntMaxTxtLen); CCLabelTTF* getLabel(){ return m_label; }; : protected: CCLabelTTF* m_label; // ラベル : </pre> <pre class="prettyprint"> // DQ風ウィンドウマネージャ "SSMsgBoxDQ.cpp" SSMsgBoxDQ::SSMsgBoxDQ(void) { // コンストラクタ CCLog(__FUNCTION__); } SSMsgBoxDQ::~SSMsgBoxDQ(void) { // デストラクタ CC_SAFE_RELEASE(m_label); } SSMsgBoxDQ* SSMsgBoxDQ::createWithArgs(kSSMsgBoxType type, float fontSize, int cntCols, int cntRows, int cntMaxTxtLen) { // 初期化関数 SSMsgBoxDQ *pRet = new SSMsgBoxDQ(); if (pRet && pRet->initWithArgs(type, fontSize, cntCols, cntRows, cntMaxTxtLen)) { pRet->autorelease(); pRet->retain(); //※ 後に追加した部分 ※ return pRet; } else { CC_SAFE_DELETE(pRet); return NULL; } } </pre> このようにクラスを定義し、実際のゲームレイヤの初期化処理 Game::init() 内でメッセージボックスを生成。そして、たとえば画面タッチの Game::ccTouchEnded で表示内容を更新。ということを実験していたのだが、何度やっても更新されない。 <pre class="prettyprint"> void Game::Initialize() { : // 前略 m_statusBox = SSMsgBoxDQ::createWithArgs(kSSMsgBoxTypeStatus, 13, m_cntPlayers, 3, 7); // メッセージボックス生成 : } void Game::ccTouchEnded(cocos2d::CCTouch *pTouch, cocos2d::CCEvent *pEvent) { : // 前略 // モンスターの HPやMPを更新 m_statusBox->updateVal(i, 1, tmpMonster->hp()); m_statusBox->updateVal(i, 2, tmpMonster->mp()); : } </pre> そこでブレークポイントを作って動かしてみると、どうも init()内と、ccTouchEnded()内とで、ポインタ m_statusBox の指す内容が違う。というか ccTouchEnded()でオブジェクトが壊れている。ので、何日かハマった気がする。(8月末のことだが今思い出して書いています) 今でこそ慣れたものの、当時はまだc++を思い出し思い出しだったのでさんざ調べてみたところ、やっと見つけた。英語だが、<a href="http://www.cocos2d-x.org/wiki/Reference_Count_and_AutoReleasePool_in_Cocos2d-x">こちらのサイト</a>がたいへん参考になった。いわく: <blockquote>The logic of CCAutoreleasePool is that, when you call object->autorelease(), this object is put into the autorelease pool. The pool can help you to retain this object's lifecycle till the end of current message loop. At the end of current message loop, if this object hasn't been retained by any other class/container, it will be released automaticaly. For example, layer->addChild(sprite), the sprite is added to the 'children list' of the layer, its lifecycle is retained till the release of layer, but not the end of current message loop.</blockquote> 要点を書くと「autoreleaseによってオートリリースプールにオブジェクトが保持される。それによってオブジェクトは『今のメッセージループの終わりまで』保持され、もしこれらが他のクラスやコンテナによってリテインされなければその時に自動解放される。」たとえばレイヤーにaddChildすれば、そのライフサイクルはレイヤー解放までだが、現在のメッセージループの終わりまでではない。 ※ さらに分かりやすい解説が 上記英語記事の"An Error Sample"という項に書いてあるので具体例をご覧になれば理解が深まると思う。(まるまる引用してもアレなので割愛させて頂きます) 私はこの「メッセージループ」という表現が聞きなれなかったのだが、ようはイベントというか、上記ゲームレイヤでいうところの init とか ccTouchEnded 関数などのことで、retain をしない/されないでいると、関数の終わりのところでオブジェクトが自動解放されてしまうよ、ということなのだ。解放されたポインタで何かしようとするとクラッシュします。 そこで、対応として、createWithArgs 関数のクラス初期化処理に pRet->retain(); を追加したところ(最初はこの処理を入れてなかったため)、意図した通りにオブジェクトは解放されずに動いてくれた。 なお、このことは<a href="http://www.atmarkit.co.jp/ait/articles/1304/23/news009_3.html">こちらの日本語の記事</a>、「自動解放したくない場合は」という項にも書いてあったのだが、その「根拠」がよくわからなかったので先の英語記事に辿り着いた次第。参考になれば幸いです。
No comments:
Post a Comment
Newer Post
Older Post
Home
Subscribe to:
Post Comments (Atom)
No comments:
Post a Comment