cocos 2 dxメモリ管理

21406 ワード

今日はcococos 2 dxのメモリ管理を探ってみたいので、まずはCCObjectから始めましょう
class CC_DLL CCObject : public CCCopying

{

public:

    // object id, CCScriptSupport need public m_uID

    unsigned int        m_uID;

    // Lua reference id

    int                 m_nLuaID;

protected:

    // count of references

    unsigned int        m_uReference;

    // count of autorelease

    unsigned int        m_uAutoReleaseCount;

public:

    CCObject(void);

    virtual ~CCObject(void);

    

    void release(void);

    void retain(void);

    CCObject* autorelease(void);

    CCObject* copy(void);

    bool isSingleReference(void);

    unsigned int retainCount(void);

    virtual bool isEqual(const CCObject* pObject);



    virtual void update(float dt) {CC_UNUSED_PARAM(dt);};

    

    friend class CCAutoreleasePool;

    

    unsigned int getAutoReleaseCount();

};

以上がCCObjectの定義です.CCObjectには主に2つの保護メンバーがあります.m_uReferenceとm_uAutoReleaseCount、この2人のメンバーはいったい何の役に立つのでしょうか.まず、CCObjectの構造関数を見てみましょう.
CCObject::CCObject(void)

: m_nLuaID(0)

, m_uReference(1) // when the object is created, the reference count of it is 1

, m_uAutoReleaseCount(0)

{

    static unsigned int uObjectCount = 0;



    m_uID = ++uObjectCount;

}

構造関数からm_がわかりますか?uReferenceは1に初期化され、m_uAutoReleaseCountは0に初期化され、CCObjectは静的符号なし整数変数:uObjectCountを維持してCCObjectインスタンスの合計数を記録します.次にrelease()とretain()関数を見てみましょう.
 
void CCObject::release(void)

{

    if (m_uReference == 0) {//modify by yangm

        CCLOG("CCObject::release reference count should greater than 0");

        return;

    }

    CCAssert(m_uReference > 0, "reference count should greater than 0");

    --m_uReference;

    

    if (m_uReference == 0)

    {

        delete this;

    }

}

release()関数では、release()を実行するたびにm_が表示されます.uReferenceは自己減一し、m_uReferenceがゼロの場合、CCObjectオブジェクトは解放されます.
void CCObject::retain(void)

{

    if (m_uReference == 0) {//modify by yangm

        CCLOG("CCObject::retain reference count should greater than 0,m_uReference=%d",m_uReference);

        return;

    }

    CCAssert(m_uReference > 0, "reference count should greater than 0");



    ++m_uReference;

}

retain()関数とrelease()関数の機能は正反対で、retain()を呼び出すたびにm_uReferenceは自分で1つ増えます.次にautorelease()関数を見てみましょう.
CCObject* CCObject::autorelease(void)

{

    CCPoolManager::sharedPoolManager()->addObject(this);

    return this;

}

void CCPoolManager::addObject(CCObject* pObject)

{

    getCurReleasePool()->addObject(pObject);  // CCAutoreleasePool , CCObject CCAutoreleasePool 

}

ここでは、CCAutoreleasePoolタイプのスタックが内部にあるCCPoolManagerクラスについて説明しますが、CCPoolManagerとは何ですか?実はCCPoolManagerはCCArrayの成長可能な配列で、addObject(CCObject*pObject)関数を見てみましょう.
 
void CCAutoreleasePool::addObject(CCObject* pObject)

{

    m_pManagedObjectArray->addObject(pObject);  // CCArray CCObject 



    CCAssert(pObject->m_uReference > 1, "reference count should be greater than 1");

    ++(pObject->m_uAutoReleaseCount);   //CCObject m_uAutoReleaseCount 

    pObject->release(); // no ref count, in this case autorelease pool added.  

}

デバッグすると、CCObjectがrelease()のときに彼のm_を発見します.uReferenceが2であると、解放されることも回避されますが、このオブジェクトインスタンスは自動解放プール(CCAutoreleasePool)に追加されたときにretainされます.検証してみましょう.次の関数を見てみましょう.
void CCArray::addObject(CCObject* object)

{

    ccArrayAppendObjectWithResize(data, object);

}

/** Appends an object. Capacity of arr is increased if needed. */

void ccArrayAppendObjectWithResize(ccArray *arr, CCObject* object)

{

    ccArrayEnsureExtraCapacity(arr, 1);

    ccArrayAppendObject(arr, object);

}



/** Appends an object. Behavior undefined if array doesn't have enough capacity. */

void ccArrayAppendObject(ccArray *arr, CCObject* object)

{

    CCAssert(object != NULL, "Invalid parameter!");

    object->retain();                           // retain 

    arr->arr[arr->num] = object;

    arr->num++;

}

次に、cococos 2 dxが無効なオブジェクトインスタンスを破棄する方法を見てみましょう.以下はプログラム全体の主なループです.
 
void CCDisplayLinkDirector::mainLoop(void)

{

    if (m_bPurgeDirecotorInNextLoop)

    {

        m_bPurgeDirecotorInNextLoop = false;

        purgeDirector();

    }

    else if (! m_bInvalid)

     {

         drawScene();

     

         // release the objects

         CCPoolManager::sharedPoolManager()->pop();        

     }

}

上記の関数から、coco 2 dxが各フレームのフレームの最後にメモリプールを自動的に解放する管理者であるCCPoolManagerのpop()関数を呼び出すことがわかりますが、この関数が主にどのような仕事をしているか見てみましょう.
 
void CCPoolManager::pop()

{

    if (! m_pCurReleasePool)

    {

        return;

    }



     int nCount = m_pReleasePoolStack->count();



    m_pCurReleasePool->clear();

 

      if(nCount > 1)

      {

        m_pReleasePoolStack->removeObjectAtIndex(nCount-1);



//         if(nCount > 1)

//         {

//             m_pCurReleasePool = m_pReleasePoolStack->objectAtIndex(nCount - 2);

//             return;

//         }

        m_pCurReleasePool = (CCAutoreleasePool*)m_pReleasePoolStack->objectAtIndex(nCount - 2);

    }



    /*m_pCurReleasePool = NULL;*/

}

主に2点を行います:1、現在の自動リリースプールをクリアして、2、現在の自動リリースプールのポインタを更新します;彼がどうやって?
 
void CCAutoreleasePool::clear()

{

    if(m_pManagedObjectArray->count() > 0)

    {

        //CCAutoreleasePool* pReleasePool;

#ifdef _DEBUG

        int nIndex = m_pManagedObjectArray->count() - 1;

#endif



        CCObject* pObj = NULL;

        CCARRAY_FOREACH_REVERSE(m_pManagedObjectArray, pObj)

        {

            if(!pObj)

                break;



            --(pObj->m_uAutoReleaseCount);

            

            if( (int)(pObj->m_uAutoReleaseCount)<0 ){

                pObj->m_uAutoReleaseCount = 0;

            }

            //(*it)->release();

            //delete (*it);

#ifdef _DEBUG

            nIndex--;

#endif

        }



        m_pManagedObjectArray->removeAllObjects();

    }

}

以上の関数は主に:1、内部のすべての要素の自動解放参照m_uAutoReleaseCountを1つ減らします.2、内部のすべての要素をクリアします.ここを見て私は驚いた:どうしてすべての要素をクリアしますか?無効なオブジェクトインスタンスを消去するのではないでしょうか.次の関数を見て、彼がどのようにremoveAllObjectsをするかを見ます.
 
void CCArray::removeAllObjects()

{

    ccArrayRemoveAllObjects(data);

}



void ccArrayRemoveAllObjects(ccArray *arr)

{

    while( arr->num > 0 )

    {

        (arr->arr[—arr->num])->release();  // —arr->num

    }

}

以上の関数は、配列内部のすべての要素をreleaseし、要素の個数を0に減らします.ここまでオブジェクト全体の作成-自動リリースプールに追加-フレームの最後のリリースのプロセスは終わり、クラスを定義したら次のように困惑しました.
class A  : public CCNode

{

private:

    CCSprite* m_pSprite1;

    CCSprite* m_pSprite2;

public:

    CREATE_FUNC(A);

    virtual bool init();

};

bool A::init()

{

    m_pSprite1 = CCSprite::create(“1.png”);   // 1

    m_pSprite2 = CCSprite::create(“2.png”);  // 2

    this->addChild(m_pSprite);  // 1 

}

実際に次のフレームになると、m_pSprite 2が指すメモリは無効です.m_Sprite 1はまだ有効です.上のプロセスはどのようにして行われていますか?この場合だけ説明できます:m_pSprite 1とm_pSprite 2はcreate時に自動リリースプールに加わって監視され、m_pSprite 1が親ノードに追加されたときにretainされ、フレームエンドreleaseのときに解放されません.CCNode::addChild(...):
 
void CCNode::addChild(CCNode *child, int zOrder, int tag)

{    

    CCAssert( child != NULL, "Argument must be non-nil");

    CCAssert( child->m_pParent == NULL, "child already added. It can't be added again");



    if( ! m_pChildren )

    {

        this->childrenAlloc();

    }



    this->insertChild(child, zOrder);



    child->m_nTag = tag;



    child->setParent(this);

    child->setOrderOfArrival(s_globalOrderOfArrival++);



    if( m_bRunning )

    {

        child->onEnter();

        child->onEnterTransitionDidFinish();

    }

}

retainがないことを発見し、insertChild関数を見続けます.
void CCNode::insertChild(CCNode* child, int z)

{

    m_bReorderChildDirty = true;

    ccArrayAppendObjectWithResize(m_pChildren->data, child);

    child->_setZOrder(z);

}

発見してもretainがなくて、引き続きcc ArrayAppendObjectWithResize関数を見ます
 
void ccArrayAppendObjectWithResize(ccArray *arr, CCObject* object)

{

    ccArrayEnsureExtraCapacity(arr, 1);

    ccArrayAppendObject(arr, object);

}

retainがなくてもccArrayAppendObject関数を見続けます
 
void ccArrayAppendObject(ccArray *arr, CCObject* object)

{

    CCAssert(object != NULL, "Invalid parameter!");

    object->retain();

    arr->arr[arr->num] = object;

    arr->num++;

}

やっとretainを発見.
まとめ:CCObjectインスタンスが作成された場合:
1.autorelease()を使用すると、現在のフレームのフレームの最後にrelease()が1回(注意が1回のみで、後で監視が削除されます)され、前に親ノードに追加した場合、フレームの最後のrelease()は解放されず、逆に非情に解放されます.親ノードに参加せずにオブジェクトを所有したい場合は、独自のretain()が必要です.
2、autorelease()がなければ、このインスタンスは自分で管理する必要があります.