深入的了解cocos2dx内存管理可以知道cocos2dx在这方面所做的动作,这样可以减少在开发的时候遇到的意料之外的错误。
cocos2dx的内存管理类名叫:CCobject。这个类用到的地方非常多,甚至可以说这个类是所有cocos2dx类的基类。虽然也有一些类没有从这个类继承内存管理功能,不过非常少。
CCObject类继承自CCCopying类,从CCCopying的名字来看主要是提供对象拷贝的重载,类的定义也证实了这一点。
class CC_DLL CCCopying
{
public:
virtual CCObject* copyWithZone(CCZone* pZone);
};
cocos2dx的内存管理分为2种,一种是基于引用计数的判断删除对象,一种是每绘制一帧后来进行自动释放对象。另外,cocos2dx的内存管理并不像boost的智能指针那样,在对象离开作用域之后自动删除内存(引用计数),在cocos2dx里边如果要删除对象还需要显示的调用CCObject::release()函数。
第一种内存管理主要是通过CCObject对象的两个函数来进行:retain()和release()。对象调用retain使得引用计数增加1(在创建的时候引用计数初始化为1),release使得对象引用计数减一,但不会立即删除内存,只有对象持有的引用计数等于0的时候才会删除对象。
CCObject::CCObject(void)
: m_nLuaID(0)
, m_uReference(1) // when the object is created, the reference count of it is 1
, m_uAutoReleaseCount(0)
{
static unsigned int uObjectCount = 0;
m_uID = ++uObjectCount;
}
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;
}
第二种内存管理初看略显复杂,但是理清楚之后却觉得简单了。还记得在mainloop()函数中,有这么一行代码:
CCPoolManager::sharedPoolManager()->pop();
这一行的作用是release掉当前栈顶内存池里边 所有的对象,而且需要注意的是在函数里边只是调用release()函数而已并没有直接delete掉对象。贴一下pop函数的代码以及注释。这里有一个意外的情况,那就是如果在对象池释放对象之前对象被删除了怎么办?会不会造成野指针呢?答案是不会,在CCObject函数里边会检查m_uAutoReleaseCount变量,如果大于0那么就从对象池里边删除掉该对象。
CCObject::~CCObject(void)
{
// if the object is managed, we should remove it
// from pool manager
if (m_uAutoReleaseCount > 0)
{
CCPoolManager::sharedPoolManager()->removeObject(this);
}
// if the object is referenced by Lua engine, remove it
if (m_nLuaID)
{
CCScriptEngineManager::sharedManager()->getScriptEngine()->removeScriptObjectByCCObject(this);
}
else
{
CCScriptEngineProtocol* pEngine = CCScriptEngineManager::sharedManager()->getScriptEngine();
if (pEngine != NULL && pEngine->getScriptType() == kScriptTypeJavascript)
{
pEngine->removeScriptObjectByCCObject(this);
}
}
}
贴一下CCPoolManager::sharedPoolManager()->pop(); 的代码
void CCPoolManager::pop()
{
//当前栈顶内存池是否有分配。
if (! m_pCurReleasePool)
{
return;
}
//取得栈里边所有内存池数量。
int nCount = m_pReleasePoolStack->count();
//释放掉当前栈顶内存池内存,调用release(),并没有直接delete this。
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_pCurReleasePool->clear()源码及注释
void CCAutoreleasePool::clear()
{
//内存池对象数组。
if(m_pManagedObjectArray->count() > 0)
{
//CCAutoreleasePool* pReleasePool;
#ifdef _DEBUG
int nIndex = m_pManagedObjectArray->count() - 1;
#endif
CCObject* pObj = NULL;
//循环释放掉autoreleasecount标记。
CCARRAY_FOREACH_REVERSE(m_pManagedObjectArray, pObj)
{
if(!pObj)
break;
--(pObj->m_uAutoReleaseCount);
//(*it)->release();
//delete (*it);
#ifdef _DEBUG
nIndex--;
#endif
}
//释放掉所有保存的对象。调用release()
m_pManagedObjectArray->removeAllObjects();
}
}
看一下
m_pManagedObjectArray->removeAllObjects()里边发生了什么。
void CCArray::removeAllObjects()
{
ccArrayRemoveAllObjects(data);
}
/** Removes all objects from arr */
void ccArrayRemoveAllObjects(ccArray *arr)
{
while( arr->num > 0 )
{
(arr->arr[--arr->num])->release();
}
}
如果需要将对象放到内存池进行自动释放管理只需要调用CCObject::autorelease()就可以了。
至此,cocos2dx的内存管理分析到这了,希望大家能有所收获^_^!