閱讀457 返回首頁    go 技術社區[雲棲]


從CCObject看cocos2d-x的內存管理機製

再看CCObject,剔除上節的拷貝相關,以及Lua腳本相關的屬性和方法後,CCObject還剩下什麼?


1.剩下什麼?

可以看到整個CCObject就是圍繞著m_uReference和m_uAutoReleaseCount在轉。這兩個變量的解釋如下。所以CCObject剩下的其實就是對內存的管理。

  1. CCObject::CCObject(void)//構造函數  
  2. : m_nLuaID(0)  
  3. , m_uReference(1) //引用計數,初始為1,當引用計數=0,自動釋放該對象  
  4. , m_uAutoReleaseCount(0)//自動釋放對象計數,如果是手動new的,則為0,如果autoRelease的,則在AutoreleasePool會+1  
  5.                         //這裏是保護成員,所以CCAutoreleasePool被聲明為友元類  
  6. {  
  7.     static unsigned int uObjectCount = 0;//靜態成員,對象的計數,隻增不減,用於標識唯一一個對象實例  
  8.     m_uID = ++uObjectCount;                
  9. }  
  10.   
  11. CCObject::~CCObject(void)  
  12. {  
  13.     if (m_uAutoReleaseCount > 0)//如果是自動管理,則在PoolManager中刪除  
  14.     {  
  15.         CCPoolManager::sharedPoolManager()->removeObject(this);  
  16.     }  
  17.   
  18.     // if the object is referenced by Lua engine, remove it  
  19.     if (m_nLuaID)  
  20.     {  
  21.         CCScriptEngineManager::sharedManager()->getScriptEngine()->removeScriptObjectByCCObject(this);  
  22.     }  
  23.     else  
  24.     {  
  25.         CCScriptEngineProtocol* pEngine = CCScriptEngineManager::sharedManager()->getScriptEngine();  
  26.         if (pEngine != NULL && pEngine->getScriptType() == kScriptTypeJavascript)  
  27.         {  
  28.             pEngine->removeScriptObjectByCCObject(this);  
  29.         }  
  30.     }  
  31. }  
  32.   
  33. void CCObject::release(void)//引用計數-1,如果引用計數=0,釋放對象  
  34. {  
  35.     CCAssert(m_uReference > 0, "reference count should greater than 0");  
  36.     --m_uReference;  
  37.   
  38.     if (m_uReference == 0)  
  39.     {  
  40.         delete this;  
  41.     }  
  42. }  
  43.   
  44. void CCObject::retain(void)//引用計數+1,防止被對象釋放  
  45. {  
  46.     CCAssert(m_uReference > 0, "reference count should greater than 0");  
  47.   
  48.     ++m_uReference;  
  49. }  
  50.   
  51. CCObject* CCObject::autorelease(void)//對象加入PoolManager,自動管理  
  52. {  
  53.     CCPoolManager::sharedPoolManager()->addObject(this);  
  54.     return this;  
  55. }  
  56.   
  57. bool CCObject::isSingleReference(voidconst //返回是否唯一引用  
  58. {  
  59.     return m_uReference == 1;  
  60. }  
  61.   
  62. unsigned int CCObject::retainCount(voidconst  //返回引用計數  
  63. {  
  64.     return m_uReference;  
  65. }  
  66.   
  67. bool CCObject::isEqual(const CCObject *pObject) //判斷對象是否相等  
  68. {  
  69.     return this == pObject;  
  70. }  
  71.   
  72. void CCObject::acceptVisitor(CCDataVisitor &visitor) //輔助對象執行動作  
  73. {  
  74.     visitor.visitObject(this);  
  75. }  

2.內存管理

從CCObject可以看出,內存的管理方式有兩種:手動管理和自動管理。


2.1.手動內存管理

想必從Java轉到C++的朋友可能很受不了C++再申請完內存後還要手動釋放,就像我從C++轉Java時也同樣很不習慣竟然不用管理內存,老是害怕會不小心讓係統給銷毀了。CCObject的成員變量m_uAutoReleaseCount標識了是手動管理還是自動管理。如果執行以下操作:

  1. CCObject* obj=new CCObject();//m_uAutoReleaseCount=0,m_uReference=1  
從析構函數可以看到,析構函數是不對所有手動進行申請的變量進行內存釋放(必須m_uAutoReleaseCount>0)。那麼這時得手動釋放:

  1. obj->release();//m_uReference-1後為0,執行delete this;  
所以,new和release是好基友!而手動內存管理一般不再使用retain。

2.2.自動內存管理

如果需要進行內存的自動管理,那要怎麼做呢?

  1. CCObject* obj=new CCObject();//m_uAutoReleaseCount=0,m_uReference=1  
  2. obj->autorelease();//m_uAutoReleaseCount=1,m_uReference=1  

好了,什麼都不用做,obj自生自滅了。

如果我們需要隨時用到obj,而不願意讓它在我們不知情的情況下被釋放,那麼使用:

  1. obj->retain();//m_uAutoReleaseCount=1,m_uReference=2  
當不再需要它的時候,使用:

  1. obj->release();//m_uAutoReleaseCount=1,m_uReference=1  

又恢複回去了,所以,retain和release是好基友,一般在自動內存管理使用。


這裏隻是大概寫一下如何使用new,autorelease,retain和release,至於內存管理的實現網上的代碼解析很多,發現自己沒辦法深入淺出地寫出來,所以還是放棄再寫一回注釋了,原理可以這麼理解:Cocos2d-x提供了一個內存管理器類CCPoolManager,它包含了一個CCArray容器m_pReleasePoolStack,這個容器用來存放一些容器管理類CCAutoreleasePool的實例對象。需要自動進行內存釋放的CCObject實例對象會把其指針存放在容器管理類CCAutoreleasePool的實例對象中的m_pManagedObjectArray容器裏。所有存在其中的CCObject實例對象在進行釋放操作時通過使用計數器來進行判斷在何時真正釋放內存,遊戲在每一幀結束時都會對autorelease對象進行釋放。

2.3.一個疑問

平時我們可能會這麼用:

  1. //CTestLayer.h  
  2. class CTestLayer : public cocos2d::CCLayer  
  3. {  
  4. public:  
  5.     virtual bool init();  
  6.   
  7.     CREATE_FUNC(CTestLayer);  
  8.   
  9.     virtual void update(float delta);  
  10.   
  11. private:  
  12.     CCSprite* background;  
  13. };  
  14.   
  15. //CTestLayer.cpp  
  16. bool CTestLayer::init()  
  17. {  
  18.     if ( !CCLayer::init() )  
  19.     {  
  20.         return false;  
  21.     }  
  22.     background=CCSprite::create("background.png");  
  23.     this->addChild(background);  
  24.     this->scheduleUpdate();  
  25.     return true;  
  26. }  
  27.   
  28. void update(float delta)  
  29. {  
  30.     background->setPositionY(background->getPositionY()-0.1);  
  31. }  
background是create出來的,可以知道它是調用了autorelease,屬於自動管理對象,而且我們又沒有進行retain操作,按道理在執行update的時候background已經是要被銷毀的,但是實際並沒有。問題就出在這一句:

  1. this->addChild(background);  

至於為什麼,大家翻一下addChild實現源碼就知道啦~

最後更新:2017-04-03 12:56:33

  上一篇:go android:descendantFocusability用法簡析
  下一篇:go 數據庫基礎