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();
這樣就不會讓自動釋放的對象集中在一幀結束的時候。