天天看點

Cocos2d-x與ios記憶體管理分析(在遊戲中減少記憶體壓力)

Cocos2d-x與ios記憶體管理分析(在遊戲中減少記憶體壓力)​

猴子原創,歡迎轉載。

注:自己以前也寫過Cocos2d-x如何優化記憶體的使用,以及記憶體不足的情況下怎麼處理遊戲。今天在微網誌中看到有朋友介紹了下記憶體,挺詳細的。不知道是誰寫的,我記錄下。

一,iOS與圖檔記憶體

在iOS上,圖檔會被自動縮放到2的N次方大小。比如一張1024*1025的圖檔,占用的記憶體與一張1024*2048的圖檔是一緻的。圖檔占用記憶體大小的計算的公式是;長*寬*4。這樣一張512*512 占用的記憶體就是 512*512*4 = 1M。其他尺寸以此類推。(ps:IOS上支援的最大尺寸為2048*2048)。

二,Cocos2d-x的圖檔緩存

Cocos2d-x 在構造一個精靈的時候會使用spriteWithFile或者spriteWithSpriteFrameName等 無論用哪種方式,Cocos2d-x都會将這張圖檔加載到緩存中。如果是第一次加載這個圖檔,那就會先将這張圖檔加載到緩存,然後從緩存讀取。如果緩存中已經存在,則直接從緩存中提取,免除了加載過程。

圖檔的緩存主要由以下兩個類來處理:CCSpriteFrameCache, CCTextureCache

CCSpriteFrameCache加載的是一張拼接過的大圖,每一個小圖隻是大圖中的一個區域,這些區域資訊都在plist檔案中儲存。用的時候隻需要根據小圖的名稱就可以加載到這個區域。

CCTextureCache 是普通的圖檔緩存,我們所有直接加載的圖檔都會預設放到這個緩存中,以提高調用效率。

是以,每次加載一張圖檔,或者通過plist加載一張拼接圖時,都會将整張圖檔加載到記憶體中。如果不去釋放,那就會一直占用着。

三,渲染記憶體。

不要以為,計算記憶體時,隻計算加載到緩存中的記憶體就可以了。以一張1024*1024的圖檔為例。

CCSprite *pSprite = CCSprite::spriteWithFile("a.png");

調用上邊這行代碼以後,可以在LEAKS工具中看到,增加了大約4M的記憶體。然後接着調用

addChild(pSprite);

這時,記憶體又增加了4M。也就是,一張圖檔,如果需要渲染的話,那它所占用的記憶體将要X2。

再看看通過plist加載的圖檔,比如這張大圖尺寸為2048*2048。想要加載其中的一張32*32的小圖檔

CCSpriteFrameCache::sharedSpriteFrameCache()->addSpriteFramesWithFile("b.plist");

此時記憶體增加16M(汗)

CCSprite *pSpriteFrame= CCSprite::spriteWithSpriteFrameName("b1.png");

b.png 大小為32*32,想着也就是增加一點點記憶體,可實際情況是增加16M記憶體。也就是隻要渲染了其中的一部分,那麼整張圖檔都要一起被加載。

但是情況不是那麼的糟糕,這些已經渲染的圖檔,如果再次加載的話,記憶體是不會再繼續升高的,比如又增加了100個b.plist的另一個區域,圖檔記憶體還是共增加16+16 = 32M,而不會繼續上升。

四,緩存釋放

如果遊戲有很多場景,在切換場景的時候可以把前一個場景的記憶體全部釋放,防止總記憶體過高.

CCTextureCache::sharedTextureCache()->removeAllTextures();釋放到目前為止所有加載的圖檔

CCTextureCache::sharedTextureCache()->removeUnusedTextures();将引用計數為1的圖檔釋放掉CCTextureCache::sharedTextureCache()->removeTexture(); 單獨釋放某個圖檔

CCSpriteFrameCache 與 CCTextureCache 釋放的方法差不多。

值得注意的是釋放的時機,一般在切換場景的時候釋放資源,如果從A場景切換到B場景,調用的函數順序為B::init()---->A::exit()---->B::onEnter()。

可如果使用了切換效果,比如CTransitionJumpZoom::transitionWithDuration這樣的函數,則函數的調用順序變為B::init()---->B::onEnter()---->A::exit() 。

而且第二種方式會有一瞬間将兩個場景的資源疊加在一起,如果不采取過度,很可能會因為記憶體吃緊而崩潰。

有時強制釋放全部資源時,會使某個正在執行的動畫失去引用而彈出異常,可以調用CCActionManager::sharedManager()->removeAllActions();來解決。 

五,記憶體優化

優化的心得就是盡量去拼接圖檔,使圖檔邊長盡可能的保持2的N次方并且裝的很滿。但要注意,有邏輯關系的圖檔盡量打包在一張大圖裡,另外一點就是打包的時候要考慮到層的分布。因為為了渲染效率可能會用到CCSpriteBatchNode;同一個BatchNode裡的圖檔都是位于一個層級的,是以必須根據各個圖檔的層級關系,打包到不同的plist裡。有時記憶體和效率不可以兼得,隻能盡量平衡了。

六,其他