您的位置:首页 > 移动开发 > Cocos引擎

cocos2dx 3.x 自学笔记 <三> cocos2dx 2.x 3.x 内存管理机制

2014-11-12 21:05 295 查看
转载请注明来自 _鞋男blog:/article/7618210.html

仍然是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 : _鞋男 /article/7618210.html

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(),看起来清爽多了 ,简简单单
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: