View on GitHub

Wills' blog

  • home | github | resume |
  • cocos-2dxメモリ管理

    19 Feb 2014

    cppで実現されたcoco2d-xのメモリについて、cppの言語自身動的なメモリを自動的に開放できない。 auto_ptr、smart_ptrを使わず、object-cのようなaotoReleasePoolをcount referenceで実現しました。

    count referenceをCCObject.hに関連定義

    class CC_DLL Object
    {
    public:
        /// object id, ScriptSupport need public _ID
        unsigned int _ID;
        /// Lua reference id
        int _luaID;
    protected:
        /// count of references
        unsigned int _referenceCount;
    

    全てのクラスはCCObject(CCScene, CCSprite, CCLayerとか)をベースとして継承したクラスは、count referenceの仕組みがある。

    autoreleaseをCCObject.cppに関連実現

    Object* Object::autorelease()
    {
        PoolManager::getInstance()->getCurrentPool()->addObject(this);
        return this;
    }
    

    呼び出すとCCObjectはPoolManagerに管理される。PoolManager実際の動作は、毎フレームの時ループでPoolにあるObjectをチェックした、reference数によって処理を行う。つまり、CCObjectのinstanceをnewした後、autoreleaseメッソドを呼び出すと、メモリを管理しなくていい。

    CREATE_FUNCについて

    実際使う時new initじゃなくてcreateを使う。CLASS.hによく使うCREATE_FUNCメソッドはCCPlatformMacros.hに定義した。

    #define CREATE_FUNC(__TYPE__) \
    static __TYPE__* create() \
    { \
        __TYPE__ *pRet = new __TYPE__(); \
        if (pRet && pRet->init()) \
        { \
            pRet->autorelease(); \
            return pRet; \
        } \
        else \
        { \
            delete pRet; \
            pRet = NULL; \
            return NULL; \
        } \
    }
    

    つまり、終期化した後、autoreleaseを使うこと。そして、createを使うとautoreleaseを呼ばなくていい。

    retainについて

    CCObject.hにretainを定義した。

        inline void retain()
        {
            CCASSERT(_referenceCount > 0, "reference count should greater than 0");
            ++_referenceCount;
        }
    

    それに対して、releaseメソッドは_referenceCountを減らす(0の時メモリを解放する)。

    addChildについて

    前話したautoreleasepoolについては、createしたinstance(背景かと、主人公とか)何もしない場合、次のフレームの時autoreleasepoolによるreleaseされる、エラーになる。その時CCNodeのaddChildメッソドを使う。名前通り、子供として追加すること。3つの形のaddhildがあるんですが、全てはChildrenのCCVectorにpushBackすること。

    CCVector.h

        void pushBack(T object)
        {
            CCASSERT(object != nullptr, "The object should not be nullptr");
            _data.push_back( object );
            object->retain();
        }
    

    中に object->retain()を使っている。それに対してpopBackにreleaseを使っている。要するに、addChildを使うと、

    1. ~parentのときは、ChildrenのCCVectorを削除する。つまりaddChildで追加したchildはparentが存在するchildも存在する(フレームに関係なく)
    2. 手動的にretain,releaseしなくていい、すでにparentが管理している

    手動的にretainしたい場合

    手動的のretainしたい場合もなる。永続かしたい、開発者がObjectのLift circleをコントロールしたい場合、手動的にretainを使う。retainを手動で呼ぶと、autoreleasepoolは削除できない(_referenceCountを0にできないから)、メモリリークを発生しまう。そして必ずpairとしてreleaseを使って、CCObjectのメモリをreleaseする。

    まとめ

    1. new, initを避けてcreateを使う
    2. retainした後必ずrelease
    3. addChildを使ってretainを使わない場合、メモリを管理しなくていい(ただしremoveChildは必要)

    メモとして、参考Cococs2dxバージョン:3.0.0betarsion: cocos2dx3.0-beta


    [click to comment]