对cocos2dx内存管理机制的一些理解:
知识点1:主循环,启动游戏之后,在导演类的控制下,每帧执行一次循环。每一次循环所做的事情分为三段:1、判断是否需要释放CCDirector,如果需要则删除CCDirector所占用的资源。通常游戏结束时才执行这个步骤。2、调用drawscene绘制场景(分三部分解释绘制场景:首先掉用定时器触发事件然后判断是否切换场景,使用setNextStage切换最后调用visit绘制当前场景,当然实际过程要比这复杂的多,包括引擎脚本和OPenGL绘图)3、调用内存池管理类的pop函数弹出内存池,使得回收池中的所有对象释放一次。
知识点2:两种内存管理机制,1、引用计数,即创建对象之后计数1,之后被引用一次加1(retain),被释放一次减一(release),如果引用计数为零则在release中delete对象。2、自动内存池管理:在每一帧开始时创建一个内存池,把需要自动释放的对象指针加入内存池中,每一帧检测一次,当对象不再使用时则调用release释放掉。
执行释放内存操作的只有release中的delete方法。其他的都是对引用计数的加减。
从autorelease开始追踪执行:
CCObject* CCObject::autorelease(void)
{ CCPoolManager::sharedPoolManager()->addObject(this);//CCPoolManager是一个单例类,类似于CCDirector
return this;
}
转到addObject(this)
void CCPoolManager::addObject(CCObject* pObject)
{ getCurReleasePool()->addObject(pObject);//含有两布操作,getCurReleasePool(),然后addObject(pObject)
}
先看getCurReleasePool
CCAutoreleasePool* CCPoolManager::getCurReleasePool()
{ if(!m_pCurReleasePool) //指向当前的自动释放池,若为NULL,则执行push,在内存池栈(动态数组)中压入一个内存池
{ push();
}
CCAssert(m_pCurReleasePool, "current auto release pool should not be null");
return m_pCurReleasePool;
}
附push代码
void CCPoolManager::push()
{ CCAutoreleasePool* pPool = new CCAutoreleasePool(); //ref = 1 ,创建内存池,引用计数为1
m_pCurReleasePool = pPool;
m_pReleasePoolStack->addObject(pPool); //ref = 2 ,addObject操作pPool引用计数+1 =2
pPool->release(); //ref = 1 pPool引用计数减1
}
再看addObject
void CCAutoreleasePool::addObject(CCObject* pObject)
{ m_pManagedObjectArray->addObject(pObject); //内存池的数组容器中添加对象,引用计数加1
CCAssert(pObject->m_uReference > 1, "reference count should be greater than 1");
++(pObject->m_uAutoReleaseCount); //自动释放次数,下面介绍不为1的情形
pObject->release(); // no ref count, in this case autorelease pool added. } //引用计数减1
最后再看一下主循环的最后一个步骤:弹出内存池
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);//当前自动释放池指针指向第二个(可为空)。
}
}
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); //每个对象的自动释放次数减1
#ifdef _DEBUG
nIndex--;
#endif
}
m_pManagedObjectArray->removeAllObjects(); //下面转入
}
}
转入removeAllObject
void ccArrayRemoveAllObjects(ccArray *arr)
{ while( arr->num > 0 )
{
(arr->arr[--arr->num])->release();
}
}
自动回收池容器中的对象各释放一次。若对象中间没有被使用则会被释放掉,由于当前帧结束时才会释放,所以这期间有足够的时间为他添加引用。另外,再CCObject类的析构函数中也会释放一次,可以在中间调用,也会在对象离开作用域的时候释放,避免了内存泄露。
对象的指针传递(自动释放计数不为1)
CCTableViewCell *CCTableView::dequeueCell()
{ CCTableViewCell *cell;
if (m_pCellsFreed->count() == 0)
{ cell = NULL;
}
else
{ cell = (CCTableViewCell*)m_pCellsFreed->objectAtIndex(0); //对象的指针传递
cell->retain(); //引用计数加1
m_pCellsFreed->removeObjectAtIndex(0); //引用计数减1
cell->autorelease(); //当前对象的自动释放计数再加1
}
return cell;
}
自动释放计数的唯一使用地方:
void CCAutoreleasePool::removeObject(CCObject* pObject) { for (unsigned int i = 0; i < pObject->m_uAutoReleaseCount; ++i) //多次自动释放计数时(看下一行注释) //1
{ m_pManagedObjectArray->removeObject(pObject, false); //首先从自动释放池中删除所有指针(同上) //2
}
}
//转到CCArray的removeObject
void CCArray::removeObject(CCObject* object, bool bReleaseObj/* = true*/)
{ ccArrayRemoveObject(data, object, bReleaseObj); //3
}
void ccArrayRemoveObjectAtIndex(ccArray *arr, unsigned int index, bool bReleaseObj/* = true*/) //4
{ CCAssert(arr && arr->num > 0 && index < arr->num, "Invalid index. Out of bounds");
if (bReleaseObj)
{ CC_SAFE_RELEASE(arr->arr[index]); //安全释放,可能是对同一对象的多次释放.当已经释放后,第三行的remove返回空,不再向下执行.
}
arr->num--;
unsigned int remaining = arr->num - index;
if(remaining>0)
{ memmove((void *)&arr->arr[index], (void *)&arr->arr[index+1], remaining * sizeof(CCObject*));
}
}