cocos2d-x回收池原理

cocos2d-x源于cocos2d-iphone,为了与Objective-c一致,cocos2d-x也采用了引用计数与自动回收的内存管理机制。

要现实自动内存回收,需继承于cocos2d-x的根类CCObject。当然自动释放会影响性能的。

cocos2d-x中有很多静态工场方法,例如以create开头的,这些静态工场方法创建的对象都使用的autorelease,试想如果不用autorelease,

CCObject* create(){CCObject *ret = new CCObject(); return ret;}

此时函数内的引用在函数结束时就结束了,但是对方没有被释放,如果返回的ret没有被用户引用或者释放,那么就造成了内存泄露。所以加入autorelease是个不错的解决方法。

看看CCObject的这两个字段和方法:

protected: // 引用计数
    unsigned int m_uReference; // 自动释放次数 以前是bool m_bManaged;标识是否采用自动释放
    unsigned int m_uAutoReleaseCount; public: void release(void); void retain(void); CCObject* autorelease(void); unsigned int retainCount(void);

有这么一个原则:谁引用谁retain和release,对象传值时先retain再release(避免自己给自己传值时,先release可能会释放对象),调用release方法,当引用计数为0时,就会delete此对象。

内存管理autorelease是怎么实现的呢,进入autorelease看到调用了下面这个函数:

CCPoolManager::sharedPoolManager()->addObject(this);将对象加入回收池

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.
}

函数中将对象加入管理数组中,再对自动释放计数+1,然后release使引用计数-1。

当create一个对象时,new使引用计数为1,调用autorelease后,加入回收池,然后release引用计数-1。那么此时引用计数为0吗?

答案肯定不是,如果为0,此时该对象就会被delete。cocos2d-x中的数据结构转为这种内存管理设置,如CCArray,当对array调用addObject时,就会调用retain使引用计数加1,当array调用removeObject时就会release。那么当加入回收池后,此时的这个引用归回收池所有,当一帧结束释放回收池的时候,回收池中的所有对象都会调用release。如果此时没有其他对象引用该对象,则该对象会被删除。

来看看主循环mainLoop中的调用:

    if (m_bPurgeDirecotorInNextLoop) { m_bPurgeDirecotorInNextLoop = false; purgeDirector(); } else if (! m_bInvalid) { drawScene(); // release the objects
         CCPoolManager::sharedPoolManager()->pop(); }

每一帧结束调用sharedPoolManager()->pop()此时栈顶的回收池就被释放,

void CCPoolManager::pop()
{
    if (! m_pCurReleasePool)
    {
        return;
    }

     int nCount = m_pReleasePoolStack->count();

    m_pCurReleasePool->clear();
 
      if(nCount > 1)
      {
        //释放栈顶的回收池
        m_pReleasePoolStack->removeObjectAtIndex(nCount-1);
        m_pCurReleasePool = (CCAutoreleasePool*)m_pReleasePoolStack->objectAtIndex(nCount - 2);
    }
}

此时池中的所有对象都会被release,如果没有其它的对象(场景、层、数组等)在引用他们,这些对象就会被delete。

而新一帧开始后,第一个调用autorelease的对象在内部调用getCurReleasePool()时,如果栈中没有回收池,就会push一个回收池,sharedPoolManager()->push()。

CCAutoreleasePool* CCPoolManager::getCurReleasePool()
{
    if(!m_pCurReleasePool)
    {
        push();
    }
    CCAssert(m_pCurReleasePool,"current auto release pool should not be null");
    return m_pCurReleasePool;
}

可以看出每一帧回收池都要建立、删除,而且添加对象进入回收池,那么性能方面当然会受到影响,当回收池中对象很多时,表现得很明显。

那么解决方法1:性能要求高的情况下不要轻易使用自动回收池。

方法2:手动释放并创建一个回收池

CCPoolManager::shardPoolManager()->push(); for(int i=0; i!=n; ++i) { objArray[i]->autorelease(); } CCPoolManager::shardPoolManager()->pop();

这样就不会让自动释放的对象集中在一帧结束的时候。

相关文章

    本文实践自 RayWenderlich、Ali Hafizji 的文章《...
Cocos-code-ide使用入门学习地点:杭州滨江邮箱:appdevzw@1...
第一次開始用手游引擎挺激动!!!进入正题。下载资源1:从C...
    Cocos2d-x是一款强大的基于OpenGLES的跨平台游戏开发...
1.  来源 QuickV3sample项目中的2048样例游戏,以及最近《...
   Cocos2d-x3.x已经支持使用CMake来进行构建了,这里尝试...