cocos2dx 3.x 自學筆記 cocos2dx 2.x 3.x 內存管理機制

轉載請註明來自 _鞋男blog:http://blog.csdn.net/wushao126/article/details/41050605

仍然是2.x 與 3.x 的對比

cocos2dx 2.x:


當我們創建一個精靈的時候,都會調用精靈對象的autorelease(),

CCSprite* CCSprite::create(const char *pszFileName)
{
    CCSprite *pobSprite = new CCSprite();
    if (pobSprite && pobSprite->initWithFile(pszFileName))
    {
        pobSprite->autorelease();
        return pobSprite;
    }
    CC_SAFE_DELETE(pobSprite);
    return NULL;
}
當創建一個精靈對象的時候,
    CCSprite* sprite = CCSprite::create(s_pPathGrossini);
    CCLog("sprite->retainCount()=%d",sprite->retainCount());//output:sprite->retainCount()=1
    addChild(sprite, 0, kTagSprite);
    CCLog("sprite->retainCount()=%d",sprite->retainCount());//output:sprite->retainCount()=2
跟蹤
pobSprite->autorelease();
跳轉到了
CCObject* CCObject::autorelease(void)
{
    CCPoolManager::sharedPoolManager()->addObject(this);
    return this;
}

敢說CCSprite不是CCObject的派生類!!!

有個CCPoolManager全局對象,把精靈對象加了進去

void CCPoolManager::addObject(CCObject* pObject)
{
    getCurReleasePool()->addObject(pObject);
}

形參爲CCObject* ,這就已經不用管對象是不是精靈了,我們只需要記得是個CCObject對象,至於多肽神馬的有RTTI機制搞定了。

繼續跟進:

void CCPoolManager::addObject(CCObject* pObject)
{
    getCurReleasePool()->addObject(pObject);
}

CCAutoreleasePool* CCPoolManager::getCurReleasePool()
{
    if(!m_pCurReleasePool)
    {
        push();<pre name="code" class="cpp">        //創建一個自動釋放池 並加入到釋放池棧中
    }

    CCAssert(m_pCurReleasePool, "current auto release pool should not be null");

    return m_pCurReleasePool;
}


這裏簡單說明下,
class CC_DLL CCPoolManager
{
    CCArray*    m_pReleasePoolStack;  
    CCAutoreleasePool*    m_pCurReleasePool;
public:
...
    friend class CCAutoreleasePool;
};
void CCPoolManager::push()
{
    CCAutoreleasePool* pPool = new CCAutoreleasePool();       //ref = 1
    m_pCurReleasePool = pPool;

    m_pReleasePoolStack->addObject(pPool);                   //ref = 2

    pPool->release();                                       //ref = 1
}

以上代碼可以看出我們創建的對象被加入到一個CCArray中的一個CCAutoreleasePool中,看這

void CCAutoreleasePool::addObject(CCObject* pObject)
{
    m_pManagedObjectArray->addObject(pObject);

    CCAssert(pObject->m_uReference > 1, "reference count should be greater than 1");
    ++(pObject->m_uAutoReleaseCount);
    pObject->release(); // no ref count, in this case autorelease pool added.
}
其實在CCAutoreleasePool裏也維持一個CCArray,用來存放被加入的CCObject對象,m_uReference在CCObject構造器中被初始化爲1,m_uAutoReleaseCount +1了,但是autoRelease的時候未被引用,所以會被release,m_uReference被減1爲0,因爲到0的時候,那對象豈不是掛了,看下面代碼

void ccArrayAppendObject(ccArray *arr, CCObject* object)
{
    CCAssert(object != NULL, "Invalid parameter!");
    object->retain();
	arr->arr[arr->num] = object;
	arr->num++;
}
這個全局函數式CCArray::addObject調用,在這裏retain()了對象,m_uReference爲2,但是我們沒有引用,所以在這裏release了下

void CCObject::release(void)
{
    CCAssert(m_uReference > 0, "reference count should greater than 0");
    --m_uReference;

    if (m_uReference == 0)
    {
        delete this;
    }
}
至於CCArray是什麼機制,這個以後會講到的

以上基本把retain() ,release() ,autorelease()講了個遍。

接下來將引擎如果管理CCAutoreleasePool的:

int CCApplication::run(){
...
   while(1){
   ...
            if (nNow.QuadPart - nLast.QuadPart > m_nAnimationInterval.QuadPart)
            {
                nLast.QuadPart = nNow.QuadPart;
                CCDirector::sharedDirector()->mainLoop();//
            }
   ...
   }
}
CCDirector遊戲引擎的管理者,這裏介紹mainLoop(),顧名思義,主循環,任何系統都有它的死循環,這個也不例外。進去看看:

void CCDisplayLinkDirector::mainLoop(void)
{
    if (m_bPurgeDirecotorInNextLoop)
    {
        m_bPurgeDirecotorInNextLoop = false;
        purgeDirector();
    }
    else if (! m_bInvalid)
     {
         drawScene();
     
         // release the objects
         CCPoolManager::sharedPoolManager()->pop();        
     }
}
這個也沒什麼與內存有關的也就是 pop(),且看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;*/
}
上面說明了m_pReleasePoolStack存放的是CCAutoreleasePool對象,是個CCArray*,如果把這個CCArray看成一個棧,就將棧頂彈出,addObject 會調用retain(),同理,removeObject會調用一次release(),但凡棧內m_uReference==1的對象將會被釋放掉,因爲沒有被引用。
若連續兩次autorelease會怎樣?

cocos2dx  2.x.x 內存管理我也只瞭解這麼多,歡迎留言探討。


原創BLOG : _鞋男 http://blog.csdn.net/wushao126/article/details/41050605


cocos2dx 3.x 內存管理機制與2.x 差不多,數據結構改變了,用vector<> 代替了 CCArray ,在addObject是無需retain 再release,實現方便了不少

void DisplayLinkDirector::mainLoop()
{
    if (_purgeDirectorInNextLoop)
    {
        _purgeDirectorInNextLoop = false;
        purgeDirector();
    }
    else if (! _invalid)
    {
        drawScene();
     
        // release the objects
        PoolManager::getInstance()->getCurrentPool()->clear();
    }
}
void AutoreleasePool::clear()
{
#if defined(COCOS2D_DEBUG) && (COCOS2D_DEBUG > 0)
    _isClearing = true;
#endif
    for (const auto &obj : _managedObjectArray)
    {
        obj->release();
    }
    _managedObjectArray.clear();
#if defined(COCOS2D_DEBUG) && (COCOS2D_DEBUG > 0)
    _isClearing = false;
#endif
}

void Ref::release()
{
    CCASSERT(_referenceCount > 0, "reference count should greater than 0");
    --_referenceCount;

    if (_referenceCount == 0)
    {
#if defined(COCOS2D_DEBUG) && (COCOS2D_DEBUG > 0)
        auto poolManager = PoolManager::getInstance();
        if (!poolManager->getCurrentPool()->isClearing() && poolManager->isObjectInPools(this))
        {
            CCASSERT(false, "The reference shouldn't be 0 because it is still in autorelease pool.");
        }
#endif

#if CC_USE_MEM_LEAK_DETECTION
        untrackRef(this);
#endif
        delete this;
    }
}
上面三段代碼是每幀PoolManager管理內存池的代碼,對照着2.x的也不難理解。

Ref* Ref::autorelease()
{
    PoolManager::getInstance()->getCurrentPool()->addObject(this);
    return this;
}
void AutoreleasePool::addObject(Ref* object)
{
    _managedObjectArray.push_back(object);
}
由於用的是STL,所以在這裏沒有個調用retain(),release(),看起來清爽多了 ,簡簡單單



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章