読者です 読者をやめる 読者になる 読者になる

WonderPlanet DEVELOPER BLOG

ワンダープラネットの開発者ブログです。モバイルゲーム開発情報を発信。

はまるかもしれない参照カウンタ

Cocos2d-x

こんにちは。

今回エンジニアブログを執筆します新卒の長屋です。

今回は初めてCocos2d-xを扱う上ではまりやすい参照カウンタ周りを解説したいと思います。

Cocos2d-xの参照カウンタとは?

・そのオブジェクトが参照されているポインタの個数を持っている。

・そのオブジェクトの参照カウンタが0がなったときそのオブジェクトは解放される。

Objective-Cを触っていた方はなじみ深いと思いますが、そうでない方で実際につまづいている方を何度か見かけました。

以下のソースをご覧ください。

// HelloWorld.h  
class HelloWorld : public cocos2d::CCLayer  
{  
    //スプライトへのポインタを持ちます  
    cocos2d::CCSprite *m_pTestSprite;  
  
    public:  
  
    //初期化処理  
    virtual bool init();  
  
    //ボタンが押されたときに呼ばれる  
    void buttonTapCallback(CCObject* pSender);  
  
    〜生成マクロ〜  
};  
//HelloWorld.cpp  
bool HelloWorld::init()  
{  
    〜ボタン類を生成する処理〜  
  
    //スプライトを生成してメンバ変数として保持  
    m_pTestSprite = CCSprite::create("Icon.png");  
  
    return true;  
}  
  
//ボタンが押されたときに呼ばれるコールバック関数です  
void HelloWorld::buttonTapCallback(CCObject* pSender)  
{  
    //スプライトを中央に配置して表示  
    CCSize size = CCDirector::sharedDirector()->getWinSize();  
    m_pTestSprite->setPosition(ccp(size.width * 0.5f,size.height * 0.5f));  
    this->addChild(m_pTestSprite,10);  
}  
  

スペースの関係上所々端折ってはいますが実装内容はボタンを押すとスプライトを表示させるという単純な処理です。

一見何も問題がなさそうに見えますがいざ実行しボタンを押してスプライトを表示させようとしてもおそらく33行目かaddChildの中で落ちてしまうと思います。

実は初期化処理終了後に m_pTestSprite が解放されてしまいます。それはなぜか?

ここで先ほどの参照カウンタが登場します。

m_pTestSpriteがcreate()関数で生成された時点でCCAutoreleasePoolクラスのなかに参照が保持され、参照カウンタが1増えます。

その後フレームワーク内の描画処理後にCCAutoreleasePoolクラスに管理されているオブジェクトが削除されるタイミングで参照カウンタが0になりpTestSpriteが消えてしまいます。

対策としてはretain()関数を読んであげることで参照カウンタを1増やすことができます。

//スプライトを生成してメンバ変数として保持  
m_pTestSprite = CCSprite::create("Icon.png");  
m_pTestSprite->retain(); //ここで参照カウンタを増やす  
  
//参照カウンタを減らすこともできる  
m_pTestSprite->release();  

自分で参照カウンタを減らしたい場合は release()関数を呼びましょう。参照カウンタが0になった時点で解放されます。
またCCArrayやCCDictionary内でオブジェクトを所持する場合も中でretain()関数が呼ばれているため勝手に解放されることは有りません。

その他にもnew演算子でオブジェクトを生成してあげることでCocos2d-xの管理を外れるので勝手に消えることはなくなります。
ただし手動で管理しなければいけなくなります。

//配列を生成  
CCArray *array = new CCArray();  
  
〜色々な処理〜  
  
//削除  
delete array;  
array = 0;  

状況によって使い分けるとよいのです。