cocos2d-x管理内存主要是通过如下三个函数来管理:
1、retain
2、release
3、autorelease
其中:
autorelease函数是将对象加入到cocos2d-x的自动内存释放池中,然后在程序mainloop中每次循环中都会通过调用自动内存释放池的pop函数来释放管理池中引用次数只有1的对象。
调用类的create函数创建对象,然后加入到autorelease中。
注意如果创建一个对象,并将他加入到内存池中之后,对象的引用次数仍然为1,如果不对对象做任何其他操作,那么将会在下一帧更新的mainloop中被内存池自动释放掉。
对于一个加入到自动释放池中的对象,如果用户还希望能够继续使用该对象,那么就必须手动调用retain函数,然后在不是用的时候调用release函数。否则就会被当作一个创建期对象在自动释放池中被delete。
当一个对象被加入另外的对象之中时,相应的add操作会调用retain,从另外的对象中移除的时候,调用release操作。
cocos2d-x中将对象分为两类:
创建期对象:调用create方法生成之后。如果不对其进行其他操作,在pop函数中将会被当成创建期对象删除。
使用期对象:当执行了pop函数从自动释放池中移除之后,如果这个时候对象没有被删除,那么进入使用期。
个人感觉这种内存释放机制并不是很科学.
使用这种方式唯一的好处就是用户不用在考虑什么时候调用delete函数了,因为不管哪种方式都会自动帮你调用delete,但是也必须保证使用对象的时候正确调用retain和release函数,否则仍然会导致内存泄漏。
说了这么多,对于程序员来说还是代码最直接,下面来看看自动内存管理到底对对象干了什么。
1、autorelease函数
CCObject* CCObject::autorelease(void)
{
CCPoolManager::sharedPoolManager()->addObject(this);
return this;
}
当对象创建之后调用autorelease函数,这个时候对象的引用计数为1。
这个函数是将对象加入内存池中。
CCPoolManager::sharedPoolManager()是一个单例设计模式,保证整个应用程序中只存在一个CCPoolManager的实例。
CCPoolManager* CCPoolManager::sharedPoolManager()
{
if (s_pPoolManager == NULL)
{
s_pPoolManager = new CCPoolManager();
}
return s_pPoolManager;
}
void CCPoolManager::addObject(CCObject* pObject)
{
getCurReleasePool()->addObject(pObject);
}
getCurRelesePool函数用于获取CCPoolManager中的可用的自动释放内存池。
CCAutoreleasePool* CCPoolManager::getCurReleasePool()
{
if(!m_pCurReleasePool)
{
push();
}
CCAssert(m_pCurReleasePool, "current auto release pool should not be null");
return m_pCurReleasePool;
}
如果有个内存池对象就直接返回,没有的话就调用push函数来创建一个CCAutoreleasePool对象,并加入CCPoolManager中。
void CCPoolManager::push()
{
CCAutoreleasePool* pPool = new CCAutoreleasePool(); //ref = 1
m_pCurReleasePool = pPool;
m_pReleasePoolStack->addObject(pPool); //ref = 2
pPool->release(); //ref = 1
}
当得到CCAutoreleasePool对象之后调用其addObject函数将对象加入自动释放内存池。
void CCAutoreleasePool::addObject(CCObject* pObject)
{
m_pManagedObjectArray->addObject(pObject);//ref = 2
CCAssert(pObject->m_uReference > 1, "reference count should be greater than 1");
++(pObject->m_uAutoReleaseCount); //m_uAutoReleaseCount = 1,表示对象被自动释放内存池管理。
pObject->release(); //ref = 1// no ref count, in this case autorelease pool added.
}
数组的添加操作代码这里就不讲述了,有兴趣的读者可以自行查阅。
说道这里对象就已经被加入自动释放内存池了,那么什么时候被删除了,请看下面的代码。
在程序的主循环中有这样一句话CCDirector::sharedDirector()->mainLoop();具体代码在对应平台的CCApplication文件的run函数中。
下面我们进入mainLoop函数。
void CCDisplayLinkDirector::mainLoop(void)
{
if (m_bPurgeDirecotorInNextLoop)
{
m_bPurgeDirecotorInNextLoop = false;
purgeDirector();
}
else if (! m_bInvalid)
{
drawScene();
// release the objects
CCPoolManager::sharedPoolManager()->pop();
}
}
第一个if语句与自动释放内存池无关,主要用于程序退出的时候做清理工作。主要看pop函数。
void CCPoolManager::pop()
{
if (! m_pCurReleasePool) //如果manager中没有自动释放池,直接返回。
{
return;
}
int nCount = m_pReleasePoolStack->count(); //获取自动释放池个数。
m_pCurReleasePool->clear(); //执行清理工作。
if(nCount > 1) //如果自动释放大于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;*/
}
下面来看clear函数,这个函数用于执行清理工作。
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); //m_uAutoReleaseCount=0前提是子调用了一次autorelease函数,表示对象不再处于自动释放池管理范围内。
//(*it)->release();
//delete (*it);
#ifdef _DEBUG
nIndex--;
#endif
}
m_pManagedObjectArray->removeAllObjects();//将对象从array内移除,这里会调用对象的release函数。
}
}
因为之前说过在创建对象、调用autorelease函数之后对象的引用计数仍然为1,所以如果对象没有调用retain函数,那么对象将会在release函数中被删除,从而达到自动内存管理的目的。
最后看看retain和release函数干了什么。
void CCObject::release(void)
{
CCAssert(m_uReference > 0, "reference count should greater than 0");
--m_uReference;
if (m_uReference == 0)
{
delete this;
}
}
void CCObject::retain(void)
{
CCAssert(m_uReference > 0, "reference count should greater than 0");
++m_uReference;
}
这两个函数一个用于增加引用计数,一个用于减小引用计数,当引用计数为0,就删除对象。
写的有点多,总之在使用cocos2d-x的时候,记得如果需要持有一个对象就要调用retain函数,不再使用的时候就调用release函数。