cocos2d-x 源碼分析 : Ref (CCObject) 源碼分析 cocos2d-x內存管理策略


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,切記。
          
         
  1. void Ref::retain()  
  2. {  
  3.     CCASSERT(_referenceCount > 0, "reference count should greater than 0");  
  4.     ++_referenceCount;  
  5. }  
  1. Ref* Ref::autorelease()  
  2. {  
  3.     PoolManager::getInstance()->getCurrentPool()->addObject(this);  
  4.     return this;  
  5. }  
  1. void Ref::release()  
  2. {  
  3.     CCASSERT(_referenceCount > 0, "reference count should greater than 0");  
  4.     --_referenceCount;  
  5.     if (_referenceCount == 0)  
  6.     {  
  7.         delete this;  
  8.     }  
  9. }  

          其中一些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 源碼分析

         
  1. void AutoreleasePool::clear()  
  2. {  
  3. #if defined(COCOS2D_DEBUG) && (COCOS2D_DEBUG > 0)  
  4.     _isClearing = true;  
  5. #endif  
  6.     for (const auto &obj : _managedObjectArray)  
  7.     {  
  8.         obj->release();  
  9.     }  
  10.     _managedObjectArray.clear();  
  11. #if defined(COCOS2D_DEBUG) && (COCOS2D_DEBUG > 0)  
  12.     _isClearing = false;  
  13. #endif  
  14. }  

AutoRealse的clear函數十分清晰,就是調用其包含的ref的release函數,然後將自己的managedObject清空。這裏需要解決的疑惑是何時調用了clear函數。打開Director的源碼
  1. void DisplayLinkDirector::mainLoop()  
  2. {  
  3.   
  4.     if (_purgeDirectorInNextLoop)  
  5.     {  
  6.         _purgeDirectorInNextLoop = false;  
  7.         purgeDirector();  
  8.     }  
  9.     else if (! _invalid)  
  10.     {  
  11.         drawScene();  
  12.        
  13.         // release the objects  
  14.         PoolManager::getInstance()->getCurrentPool()->clear();  
  15.     }  
  16. }  

在你的App運行時,每一幀開始時都是先drawScene(),各種界面上的顯示結束後,就開始clear了。

2.3 PoolManager

        std::deque<AutoreleasePool*> _releasePoolStack;
        AutoreleasePool *_curReleasePool;
        PoolManager管理着所有的autorerealsePool,並且有一個指針指向當前的releasePool。
        值得注意的是,筆者認爲3.x此處有個小bug。
        
  1. PoolManager* PoolManager::getInstance()  
  2. {  
  3.     if (s_singleInstance == nullptr)  
  4.     {  
  5.         s_singleInstance = new PoolManager();  
  6.         // Add the first auto release pool  
  7.         s_singleInstance->_curReleasePool = new AutoreleasePool("cocos2d autorelease pool");  
  8.         s_singleInstance->_releasePoolStack.push_back(s_singleInstance->_curReleasePool);  
  9.     }  
  10.     return s_singleInstance;  
  11. }  


        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的使用方式。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章