cocos2dx标准容器_cocos2dx[3.2](24)——内存管理机制
【嘮叨】
整合參考文檔。
【參考】
【內(nèi)存管理機(jī)制】
在3.x版本,Cocos2d-x采用全新的根類?Ref,實(shí)現(xiàn)Cocos2d-x 類對(duì)象的引用計(jì)數(shù)記錄。引擎中的所有類都派生自Ref。
1、引用計(jì)數(shù)
Cocos2d-x 提供引用計(jì)數(shù)管理內(nèi)存。
>?調(diào)用 retain() 方法 ? ? ? ? ? ? :令其引用計(jì)數(shù)增1,表示獲取該對(duì)象的引用權(quán)。
> 調(diào)用 release() 方法 ? ? ? ? ?:在引用結(jié)束的時(shí)候,令其引用計(jì)數(shù)值減1,表示釋放該對(duì)象的引用權(quán)。
> 調(diào)用 autorelease() 方法 :將對(duì)象放入自動(dòng)釋放池。
>?當(dāng)釋放池自身被釋放的時(shí)候,它就會(huì)對(duì)池中的所有對(duì)象執(zhí)行一次release()方法,實(shí)現(xiàn)靈活的垃圾回收。
Cocos2d-x 提供 AutoreleasePool,管理自動(dòng)釋放對(duì)象。
>?當(dāng)釋放池自身被釋放的時(shí)候,它就會(huì)對(duì)池中的所有對(duì)象執(zhí)行一次release()方法。
核心類Ref:實(shí)現(xiàn)了引用計(jì)數(shù)。//
/**
*CCRef.h
**/
class?CC_DLL?Ref
{
public:
void?retain();??????//?保留。引用計(jì)數(shù)+1
void?release();?????//?釋放。引用計(jì)數(shù)-1
Ref*?autorelease();?//?實(shí)現(xiàn)自動(dòng)釋放。
unsigned?int?getReferenceCount()?const;?//被引用次數(shù)
protected:
Ref();??????????//?初始化
public:
virtual?~Ref();?//?析構(gòu)
protected:
unsigned?int?_referenceCount;?//?引用次數(shù)
friend?class?AutoreleasePool;?//?自動(dòng)釋放池
};
/**
*CCRef.cpp
**/
//?節(jié)點(diǎn)被創(chuàng)建時(shí),引用次數(shù)為?1
Ref::Ref()?:?_referenceCount(1)
{
}
void?Ref::retain()
{
CCASSERT(_referenceCount?>?0,?"reference?count?should?greater?than?0");
++_referenceCount;
}
void?Ref::release()
{
CCASSERT(_referenceCount?>?0,?"reference?count?should?greater?than?0");
--_referenceCount;
if?(_referenceCount?==?0)
{
delete?this;
}
}
Ref*?Ref::autorelease()
{
//?將節(jié)點(diǎn)加入自動(dòng)釋放池
PoolManager::getInstance()->getCurrentPool()->addObject(this);
return?this;
}
//
Ref原理分析:
>?當(dāng)一個(gè) Ref 初始化(被new出來時(shí)),_referenceCount = 1;
>?當(dāng)調(diào)用該 Ref 的 retain() 方法時(shí),_referenceCount++;
>?當(dāng)調(diào)用該 Ref 的 release() 方法時(shí),_referenceCount--。
>?若??_referenceCount 減后為0,則 delete 該 Ref。
2、retain() 和 release() 使用
下面一段簡單的例子來學(xué)習(xí) retain() 和 release() 的使用。//
TestObject*?obj1?=?new?TestObject("testobj1");
CCLOG("obj1?referenceCount=%d",obj1->getReferenceCount());
obj1->retain();
CCLOG("obj1?referenceCount=%d",obj1->getReferenceCount());
obj1->release();
CCLOG("obj1?referenceCount=%d",obj1->getReferenceCount());
obj1->release();
//
控制臺(tái)顯示的日志如下:cocos2d: TestObject:testobj1 is created
cocos2d: obj1 referenceCount=1
cocos2d: obj1 referenceCount=2
cocos2d: obj1 referenceCount=1
cocos2d: TestObject:testobj1 is destroyed
通過例子和打印結(jié)果可以看到:
>?obj1對(duì)象創(chuàng)建后,引用計(jì)數(shù)為1;
>?執(zhí)行一次retain()后,引用計(jì)數(shù)為2;
>?執(zhí)行一次release()后,引用計(jì)數(shù)回到1;
>?再執(zhí)行一次release()后,對(duì)象會(huì)被釋放掉。
因此:
>?我們可以調(diào)用retain()方法,令其引用計(jì)數(shù)增1,表示獲取該對(duì)象的引用權(quán);
>?在引用結(jié)束的時(shí)候調(diào)用release()方法,令其引用計(jì)數(shù)值減1,表示釋放該對(duì)象的引用權(quán)。
>?直到對(duì)象的引用計(jì)數(shù)為0,釋放該對(duì)象。
3、autorelease() 使用
同樣一段簡單的例子來學(xué)習(xí)autorelease的使用,代碼如下://
TestObject*?obj?=?new?TestObject("testobj");
CCLOG("obj?referenceCount=%d",obj->getReferenceCount());
obj->autorelease();
CCLOG("obj?is?add?in?currentpool?%s",PoolManager::getInstance()->getCurrentPool()->contains(obj)?"true":"false");
CCLOG("obj?referenceCount=%d",obj->getReferenceCount());
obj->retain();
CCLOG("obj?referenceCount=%d",obj->getReferenceCount());
obj->release();
CCLOG("obj?referenceCount=%d",obj->getReferenceCount());
//obj?in?current?pool?will?be?release
Director::getInstance()->replaceScene(this);
//
控制臺(tái)顯示的日志如下:cocos2d: TestObject:testobj is created
cocos2d: obj referenceCount=1
cocos2d: obj is add in currentpool true
cocos2d: obj referenceCount=1
cocos2d: obj referenceCount=2
cocos2d: obj referenceCount=1
...
cocos2d: TestObject:testobj is destroyed
通過代碼和打印結(jié)果,我們可以看到:
>?obj對(duì)象創(chuàng)建后,引用計(jì)數(shù)為1;
>?執(zhí)行一次autorelease()后,obj對(duì)象被加入到當(dāng)前的自動(dòng)釋放池。
>?obj對(duì)象的引用計(jì)數(shù)值并沒有減1。
>?但是在下一幀開始前,當(dāng)前的自動(dòng)釋放池會(huì)被回收掉,并對(duì)自動(dòng)釋放池中的所有對(duì)象執(zhí)行一次release()操作。
>?當(dāng)對(duì)象的引用計(jì)數(shù)為0時(shí),對(duì)象會(huì)被釋放掉。
>?obj對(duì)象執(zhí)行autorelease()后,我們對(duì)其執(zhí)行了一組retain()和release()操作。
>?此時(shí)obj對(duì)象的引用計(jì)數(shù)為1,在場景切換后,當(dāng)前的自動(dòng)釋放池被回收,
>?obj對(duì)象執(zhí)行一次release()操作引用計(jì)數(shù)減為0時(shí),對(duì)象會(huì)被釋放掉。
注意:autorelease()只有在自動(dòng)釋放池被釋放時(shí)才會(huì)進(jìn)行一次釋放操作,如果對(duì)象釋放的次數(shù)超過了應(yīng)有的次數(shù),則這個(gè)錯(cuò)誤在調(diào)用autorelease()時(shí)并不會(huì)被發(fā)現(xiàn),只有當(dāng)自動(dòng)釋放池被釋放時(shí)(通常也就是游戲的每一幀結(jié)束時(shí)),游戲才會(huì)崩潰。在這種情況下,定位錯(cuò)誤就變得十分困難了。
例如,在游戲中,一個(gè)對(duì)象含有1個(gè)引用計(jì)數(shù),但是卻被調(diào)用了兩次autorelease()。在第二次調(diào)用autorelease()時(shí),游戲會(huì)繼續(xù)執(zhí)行這一幀,結(jié)束游戲時(shí)才會(huì)崩潰,很難及時(shí)找到出錯(cuò)的地點(diǎn)。
因此,我們建議在開發(fā)過程中應(yīng)該避免濫用autorelease(),只在工廠方法等不得不用的情況下使用,盡量以release()來釋放對(duì)象引用。
4、AutoreleasePool類 使用
Cocos2d-x提供AutoreleasePool,管理自動(dòng)釋放對(duì)象。
下面一段簡單的例子講解AutoreleasePool的使用,代碼如下://
TestObject*?obj2?=?new?TestObject("testobj2");
CCLOG("obj2?referenceCount=%d",obj2->getReferenceCount());
//use?AutoreleasePool
{
AutoreleasePool?pool;
obj2->retain();
CCLOG("obj2?referenceCount=%d",obj2->getReferenceCount());
obj2->release();
CCLOG("obj2?referenceCount=%d",obj2->getReferenceCount());
obj2->autorelease();
CCLOG("obj2?is?add?in?pool?%s",pool.contains(obj2)?"true":"false");
TestObject?*obj3?=?new?TestObject("testobj3");
obj3->autorelease();
CCLOG("obj3?is?add?in?pool?%s",pool.contains(obj3)?"true":"false");
}
//
控制臺(tái)輸出日志如下:cocos2d: TestObject:testobj2 is created
cocos2d: obj2 referenceCount=1
cocos2d: obj2 referenceCount=2
cocos2d: obj2 referenceCount=1
cocos2d: obj2 is add in pool true
cocos2d: TestObject:testobj3 is created
cocos2d: obj3 is add in pool true
cocos2d: TestObject:testobj2 is destroyed
cocos2d: TestObject:testobj3 is destroyed
通過代碼和輸出結(jié)果,可以看到:
>?創(chuàng)建了一個(gè)obj2對(duì)象,此時(shí)obj2對(duì)象的引用計(jì)數(shù)為1。
>?接著創(chuàng)建了一個(gè)自動(dòng)釋放池,對(duì)obj2對(duì)象執(zhí)行retain()和release()操作后,執(zhí)行autorelease()操作,此時(shí)obj2對(duì)象被加入到當(dāng)前新建的自動(dòng)釋放池中。
>?接著新建了obj3對(duì)象,并執(zhí)行autorelease()操作。同樣obj3也被加入到當(dāng)前新建的自動(dòng)釋放池中。
>?在代碼塊結(jié)束后,自動(dòng)釋放池被回收,加入自動(dòng)釋放池中的obj2和obj3執(zhí)行release()操作,引用計(jì)數(shù)減為0,被釋放銷毀。
我們可以自己創(chuàng)建AutoreleasePool,管理對(duì)象的autorelease。
我們已經(jīng)知道,調(diào)用了autorelease()方法的對(duì)象(下面簡稱"autorelease對(duì)象"),將會(huì)在自動(dòng)釋放池釋放的時(shí)候被釋放一次。雖然,Cocos2d-x已經(jīng)保證每一幀結(jié)束后釋放一次釋放池,并在下一幀開始前創(chuàng)建一個(gè)新的釋放池,但是我們也應(yīng)該考慮到釋放池本身維護(hù)著一個(gè)將要執(zhí)行釋放操作的對(duì)象列表,如果在一幀之內(nèi)生成了大量的autorelease對(duì)象,將會(huì)導(dǎo)致釋放池性能下降。因此,在生成autorelease對(duì)象密集的區(qū)域(通常是循環(huán)中)的前后,我們最好可以手動(dòng)創(chuàng)建并釋放一個(gè)回收池。
例如://
//?example?of?using?temple?autorelease?pool
{
AutoreleasePool?pool2;
char?name[20];
for?(int?i?=?0;?i?
{
snprintf(name,?20,?"object%d",?i);
TestObject?*tmpObj?=?new?TestObject(name);
tmpObj->autorelease();
}
}
//
總結(jié):
>?autorelease()的實(shí)質(zhì)是將對(duì)象加入自動(dòng)釋放池,對(duì)象的引用計(jì)數(shù)不會(huì)立刻減1,在自動(dòng)釋放池被回收時(shí)對(duì)象執(zhí)行release()。
> autorelease()并不是毫無代價(jià)的,其背后的釋放池機(jī)制同樣需要占用內(nèi)存和CPU資源。
>?過多的使用autorelease()會(huì)增加自動(dòng)釋放池的管理和釋放池維護(hù)對(duì)象存取釋放的支出。
>?在內(nèi)存和CPU資源本就不足的程序中使得系統(tǒng)資源更加緊張。
>?此時(shí)就需要我們合理創(chuàng)建自動(dòng)釋放池管理對(duì)象autorelease。
>?不用的對(duì)象推薦使用release()來釋放對(duì)象引用,立即回收。
5、特殊內(nèi)存管理
5.1、工廠方法 create()
在Cocos2d-x中,提供了大量的工廠方法創(chuàng)建對(duì)象。仔細(xì)看你會(huì)發(fā)現(xiàn),這些對(duì)象都是自動(dòng)釋放的。
下面以 Label 的 create 方法為例,代碼如下://
Label*?Label::create()
{
auto?ret?=?new?Label();
if?(ret)
{
ret->autorelease();
}
return?ret;
}
//
我們可以發(fā)現(xiàn),創(chuàng)建了一個(gè)Label的對(duì)象,并對(duì)該對(duì)象執(zhí)行autorelease()。表示該對(duì)象是自動(dòng)釋放的。細(xì)心的你會(huì)發(fā)放Layer/Scene/Sprite等類的 create() 方法都相同。
使用工廠方法創(chuàng)建對(duì)象時(shí),雖然引用計(jì)數(shù)也為1,但是由于對(duì)象已經(jīng)被放入了釋放池,因此調(diào)用者沒有對(duì)該對(duì)象的引用權(quán),除非我們?nèi)藶榈卣{(diào)用了retain()來獲取引用權(quán),否則,不需要主動(dòng)釋放對(duì)象。
5.2、Node 的 addChild() / removeChild 方法
在Cocos2d-x中,所有繼承自Node類,在調(diào)用 addChild 方法添加子節(jié)點(diǎn)時(shí),自動(dòng)調(diào)用了retain。 對(duì)應(yīng)的通過?removeChild,移除子節(jié)點(diǎn)時(shí),自動(dòng)調(diào)用了release。
調(diào)用addChild方法添加子節(jié)點(diǎn),節(jié)點(diǎn)對(duì)象執(zhí)行retain。子節(jié)點(diǎn)被加入到節(jié)點(diǎn)容器中,父節(jié)點(diǎn)銷毀時(shí),會(huì)銷毀節(jié)點(diǎn)容器釋放子節(jié)點(diǎn)。對(duì)子節(jié)點(diǎn)執(zhí)行release。如果想提前移除子節(jié)點(diǎn)我們可以調(diào)用removeChild。
在Cocos2d-x內(nèi)存管理中,大部分情況下我們通過調(diào)用 addChild/removeChild 的方式自動(dòng)完成了retain,release調(diào)用。不需再調(diào)用retain,release。
【內(nèi)存優(yōu)化】
1、內(nèi)存優(yōu)化原理
為優(yōu)化應(yīng)用內(nèi)存使用,開發(fā)人員首先應(yīng)該知什么最耗應(yīng)用內(nèi)存,答案就是紋理! 紋理幾乎會(huì)占據(jù)90%應(yīng)用內(nèi)存。所以盡量最小化應(yīng)用的紋理內(nèi)存使用,否則應(yīng)用很有可能會(huì)因?yàn)榈蛢?nèi)存而崩潰。
本節(jié)介紹Cocos2d-x游戲通用的兩條內(nèi)存優(yōu)化原理指導(dǎo)。
1.1、認(rèn)識(shí)瓶頸尋找方案
什么樣的紋理最耗應(yīng)用內(nèi)存?或這些紋理會(huì)消耗多少內(nèi)存?
當(dāng)然這個(gè)不用手動(dòng)計(jì)算,只需猜測。工具在這里已經(jīng)準(zhǔn)備好了,使用的是蘋果的工具“Allocation & Leaks”。你可以在Xcode中長按“Run”命令,選擇“ Profile ”來啟動(dòng)這兩個(gè)工具。
如下所示:
使用Allocation工具可以監(jiān)控應(yīng)用的內(nèi)存使用,使用Leaks工具可以觀察內(nèi)存的泄漏情況。
此外還可用一些代碼獲取游戲內(nèi)存使用的其他信息。
如下所示://
Sprite*?bg?=?Sprite::create("HelloWorld.png");
bg->setPosition(240,?160);
this->addChild(bg);
CCLOG("%s",?Director::getInstance()->getTextureCache()->getCachedTextureInfo().c_str());
//
調(diào)用這個(gè)代碼后,游戲便會(huì)在DEBUG模式運(yùn)行,這時(shí)你會(huì)在Xcode控制臺(tái)窗口看到一些格式工整的日志信息。//
cocos2d:?"****/HelloWorld.png"?rc=2?id=3?480?x?320?@?32?bpp?=>?600?KB
"/cc_fps_p_w_picpaths"?rc=5?id=2?999?x?54?@?16?bpp?=>?105?KB
TextureCache?dumpDebugInfo:?2?textures,?for?705?KB?(0.69?MB)
//
從上可以看到會(huì)顯示紋理的名稱、引用計(jì)數(shù)、ID、大小及每像素的位數(shù)。最重要的是會(huì)顯示內(nèi)存的使用情況。如“cc_fps_p_w_picpaths”指消耗了105KB內(nèi)存,而“HelloWorld.png”消耗了600KB內(nèi)存。
1.2、切勿過度優(yōu)化
這是一個(gè)通用的優(yōu)化規(guī)則。在優(yōu)化過程中,應(yīng)該做一些權(quán)衡取舍。因?yàn)橛袝r(shí)候圖像質(zhì)量和圖像內(nèi)存使用是處于兩級(jí)的狀態(tài)。千萬不要過度優(yōu)化!
2、內(nèi)存優(yōu)化水平
在此將ccos2d-x內(nèi)存優(yōu)化分為:三個(gè)等級(jí)。
每個(gè)等級(jí)都有不同的說明,策略也有點(diǎn)不一樣。
2.1、客戶端等級(jí)
這是最重要的的優(yōu)化等級(jí)。因?yàn)槲覀円贑ocos2d-x引擎頂層編譯游戲,引擎自身會(huì)提供一些優(yōu)化選項(xiàng)。 在這個(gè)等級(jí)我們可以進(jìn)行大部分優(yōu)化。簡而言之,我們可以優(yōu)化紋理、音頻、字體及粒子的內(nèi)存使用。
第一:看紋理優(yōu)化,為了優(yōu)化紋理內(nèi)存使用,必須知道什么因素對(duì)紋理內(nèi)存使用的影響最大。主要有3個(gè)因素會(huì)影響紋理內(nèi)存,即紋理格式(壓縮還是非壓縮)、顏色深度和大小。我們可以使用PVR格式紋理減少內(nèi)存使用。推薦紋理格式為pvr.ccz。紋理使用的每種顏色位數(shù)越多,圖像質(zhì)量越好,但是越耗內(nèi)存。所以我們可以使用顏色深度為RGB4444的紋理代替RGB8888,這樣內(nèi)存消耗會(huì)降低一半。此外超大的紋理也會(huì)導(dǎo)致內(nèi)存相關(guān)問題。所以最好使用中等大小的紋理。
第二:音頻優(yōu)化,3個(gè)因素會(huì)影響音頻文件的內(nèi)存使用,即音頻文件數(shù)據(jù)格式、比特率及采樣率。推薦使用MP3數(shù)據(jù)格式的音頻文件,因?yàn)锳ndroid平臺(tái)和iOS平臺(tái)均支持MP3格式,此外MP3格式經(jīng)過壓縮和硬件加速。背景音樂文件大小應(yīng)該低于800KB,最簡單的方法就是減少背景音樂時(shí)間然后重復(fù)播放。音頻文件采樣率大約在96-128kbps為佳,比特率44kHz就夠了。
第三:字體和粒子優(yōu)化,在此有兩條小提示:使用BMFont字體顯示游戲分?jǐn)?shù)時(shí),請(qǐng)盡可能使用最少數(shù)量的文字。例如只想要顯示單位數(shù)的數(shù)字,你可以移除所有字母。至于粒子,可以通過減少粒子數(shù)來降低內(nèi)存使用。
2.2、引擎等級(jí)
需要 OpenGL ES 及游戲引擎高手。
2.3、C++語言等級(jí)
在這個(gè)等級(jí)中,建議是編寫無內(nèi)存泄露代碼。遵循Cocos2d-x內(nèi)置的內(nèi)存管理原則,盡量避免內(nèi)存泄露。
3.、提示和技巧
(1) ? ?一幀一幀載入游戲資源
(2) ? ?減少繪制調(diào)用,使用“Auto-batching”自動(dòng)批處理。
(3) ? ?載入紋理時(shí)按照從大到小的順序
(4) ? ?避免高峰內(nèi)存使用
(5) ? ?使用載入屏幕預(yù)載入游戲資源
(6) ? ?需要時(shí)釋放空閑資源
(7) ? ?收到內(nèi)存警告后釋放緩存資源.
(8) ? ?使用紋理打包器優(yōu)化紋理大小、格式、顏色深度等
(9) ? ?使用JPG格式要謹(jǐn)慎!
(10) ?請(qǐng)使用RGB4444顏色深度16位紋理
(11) ?請(qǐng)使用NPOT紋理,不要使用POT紋理
(12) ?避免載入超大紋理
(13) ?推薦1024*1024 NPOT pvr.ccz紋理集,而不要采用RAW PNG紋理
總結(jié)
以上是生活随笔為你收集整理的cocos2dx标准容器_cocos2dx[3.2](24)——内存管理机制的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 羡慕!京东物流为一线员工补贴5亿元 还有
- 下一篇: 开发工评价程师自我_常见“自我评价”写作