cocos2dx是通过引用计数的方式管理内存的。这里涉及到4个类:CCObject,CCAutoreleasePool,CCPoolManager和CCDirector;下面介绍这4个类的情况;
CCObject是cocos2dx对象的基类(这里指继承于CCObject),里面有一个m_uReference引用计数,它提供了3个主要的方法,如下:
void CCObject::release(void) { CCAssert(m_uReference > 0, "reference count should greater than 0"); --m_uReference;//计数会减1 if (m_uReference == 0) { delete this; } } void CCObject::retain(void) { CCAssert(m_uReference > 0, "reference count should greater than 0"); ++m_uReference; } CCObject* CCObject::autorelease(void) { CCPoolManager::sharedPoolManager()->addObject(this); return this; }
CCAutoreleasePool是自动对象池,新创建的对象会放到这里面管理,它提供了2个主要方法,如下:
ps: 代码中的m_pManagedObjectArray是CCArray类型的对象,用来存储CCObject对象。
void CCAutoreleasePool::addObject(CCObject* pObject) { m_pManagedObjectArray->addObject(pObject); //CCArray::addObject()方法会使对象计数+1,具体可进去看源码 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 }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); //(*it)->release(); //delete (*it); #ifdef _DEBUG nIndex--; #endif } m_pManagedObjectArray->removeAllObjects();//removeAllObjects()方法里面会遍历里面的元素,并且调用release()的,这就意味着对象的引用计数-1; } }
CCPoolManager是自动释放池的管理类,是一个singleton,主要内容如下:
CCArray* m_pReleasePoolStack; //用来装CCAutoreleasePool*对象,可能不止一个对象池哦 CCAutoreleasePool* m_pCurReleasePool;//当前使用的对象池
void CCPoolManager::addObject(CCObject* pObject) { getCurReleasePool()->addObject(pObject); //getCurReleasePool()是取到当前使用的对象池; }
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;*/ }
在看看CCDirector,相关代码如下:
void CCDisplayLinkDirector::mainLoop(void) { if (m_bPurgeDirecotorInNextLoop) { m_bPurgeDirecotorInNextLoop = false; purgeDirector(); } else if (! m_bInvalid) { drawScene(); // release the objects CCPoolManager::sharedPoolManager()->pop(); } }可以看出在 drawScene();之后会去调用 CCPoolManager::pop();就是回收对象是在帧的结尾的
下面是一个例子,假如MyClass继承CCObject:
MyClass* MyClass::create()
{
MyClass* pRet= new MyClass();
pRet->autorelease();
return pRet;
}
当某处需要创建一个MyClass类型的对象,如下:
MyClass* pMyClass = MyClass::create();
pMyClass->retain();
........
那么整个过程是什么样子的呢,往下看
当MyClass::create();被调用后,先new MyClass();,此时引用计数为1(看下面代码);
CCObject::CCObject(void) : m_nLuaID(0) , m_uReference(1) // when the object is created, the reference count of it is 1 , m_uAutoReleaseCount(0)
{......},
然后pRet->autorelease();autorelease()就往对象池中加入一个对象:CCPoolManager::sharedPoolManager()->addObject(this); //实际上这里的引用计数有一个+1再-1的过程,最终计数不变
此时该对象引用计数还是为1;接着pMyClass->retain();后,计数+1变为2,在这帧结束前在CCDirector中调用了CCPoolManager::sharedPoolManager()->pop(); //这个方法会调用到CCAutoreleasePool::clear(),而clear方法中会遍历对象池中的元素并且都调用CCObject::release()方法,此时该对象的计数-1变为1了,这就是pMyClass->retain()的引用结果(当然如果还在其他地方也retain()的话,计数就不止1了,反正retain()过n次,计数就为n.)当pMyClass不用了,会调用release()方法,此时计数在-1变为0,被delete掉,ok,这个对象生命结束。但是可能会问,为什么需要autorelease()呢,那放过来看看,如果不用这种机制,当一个对象new后立马被delete了,那么就没法使用这个对象了。使用autorelease()从对象的new开始到帧结束前的release()之间,提供了一个让第三方引用的时机。