源碼版本來自3.x,轉載請注明
cocos2d-x 源碼分析總目錄:
http://blog.csdn.net/u011225840/article/details/31743129
1.Ref,AutoreleasePool,PoolManager
Ref中包含了一個叫referenceCount的引用計數,當一個Ref類的變量被new的時候,其referenceCount的引用計數被置為1。 其中有三個重要的操作,retain,release,autorelease,下面源碼分析時會詳細說明。 AutoreleasePool中存放在被顯示調用autorealse的ref,并且在每一幀過後調用其clear函數,顯示的調用存放在其中的ref的realse函數,然後清空自身。 PoolManager則管理着所欲的AutoreleasePool,沒有錯,不僅僅有一個AutoreleasePool,有多個AutoreleasePool。
2.源碼分析
如果你想清晰的了解cocos2d-x 3.x 的記憶體管理機制,請你一定要耐心閱讀完這裡的代碼,源代碼本身清晰易懂,何況我已經加了一些必要的中文注釋呢~
2.1 Ref源碼分析
重要的成員變量:_referenceCount,在構造函數中被置為1,切記。
void Ref::retain()
{
CCASSERT(_referenceCount > 0, "reference count should greater than 0");
++_referenceCount;
}
Ref* Ref::autorelease()
{
PoolManager::getInstance()->getCurrentPool()->addObject(this);
return this;
}
void Ref::release()
{
CCASSERT(_referenceCount > 0, "reference count should greater than 0");
--_referenceCount;
if (_referenceCount == 0)
{
delete this;
}
}
其中一些Debug或者追蹤memory leak 的宏和函數我已經去掉,其實這三個函數的本質就是這麼簡單,retain和release分别增加和減少referenceCount,并且release函數在count為0時就delete 自己。autorelease函數将Ref放入目前的AutorealsePool。 Attention Please:三個函數内部都有一個CCASSERT,要求調用時該object的referenceCout必須是大于0的。 release或者autorealse必須和new或者retain成對出現。
這裡開始講一下基本的用法,因為我們平時需要我們管理的、使用的類一般都是繼承于Node(Layer、Sprite等),看下Node的create函數吧。 在Node的create中,調用了new(Count設定為1)後,立即調用了autoRelease,将其放入AutoreleasePool中。(AutoreleasePool的源碼一會再分析) 是以當你使用了create之後,請不要在使用release或者Autorelease,除非你手動了retain一次。 但是當你把一個node1加入到另一個node2的時候,你可以了解為此時node1的refereneceCount增加了一次,但是你不需要做任何額外的操作。因為當node1被remove或者即使沒有remove操作,當node2析構的時候(會将他的child的count減1)。也就是說,整個引擎會自動管理referenceCount,隻要你不要手動的retain。
2.2AutoreleasePool 源碼分析
void AutoreleasePool::clear()
{
#if defined(COCOS2D_DEBUG) && (COCOS2D_DEBUG > 0)
_isClearing = true;
#endif
for (const auto &obj : _managedObjectArray)
{
obj->release();
}
_managedObjectArray.clear();
#if defined(COCOS2D_DEBUG) && (COCOS2D_DEBUG > 0)
_isClearing = false;
#endif
}
AutoRealse的clear函數十厘清晰,就是調用其包含的ref的release函數,然後将自己的managedObject清空。這裡需要解決的疑惑是何時調用了clear函數。打開Director的源碼
void DisplayLinkDirector::mainLoop()
{
if (_purgeDirectorInNextLoop)
{
_purgeDirectorInNextLoop = false;
purgeDirector();
}
else if (! _invalid)
{
drawScene();
// release the objects
PoolManager::getInstance()->getCurrentPool()->clear();
}
}
在你的App運作時,每一幀開始時都是先drawScene(),各種界面上的顯示結束後,就開始clear了。
2.3 PoolManager
std::deque<AutoreleasePool*> _releasePoolStack;
AutoreleasePool *_curReleasePool; PoolManager管理着所有的autorerealsePool,并且有一個指針指向目前的releasePool。 值得注意的是,筆者認為3.x此處有個小bug。
PoolManager* PoolManager::getInstance()
{
if (s_singleInstance == nullptr)
{
s_singleInstance = new PoolManager();
// Add the first auto release pool
s_singleInstance->_curReleasePool = new AutoreleasePool("cocos2d autorelease pool");
s_singleInstance->_releasePoolStack.push_back(s_singleInstance->_curReleasePool);
}
return s_singleInstance;
}
poolManager是單例模式,當第一次初始化的時候,會自動生成一個AutoreleasePool,并将其放入自己的stack中。但是,當你打開AutoreleasePool的構造函數時,發現其中已經有一個調用PoolManager::getInstance()->push(this); 通過debug跟蹤,筆者發現此時有兩個AutoRealsePool。即poolManager的stack内有兩個位置都指向同一個AutoRealsePool。感覺此處應該是一個Bug。
3. 小結
1.Ref,AutorealsePool,PoolManager是緊密相關的 2.Ref的retain、new 應該與 release或者autoRealse成對出現。 3.Node的使用方式。