轉載請註明來自 _鞋男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(),看起來清爽多了 ,簡簡單單