一,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();來解決。
五、記憶體管理
1.概述
cocos2d-x最初移植自cocos2d的objective C版本。因此,在記憶體管理上,使用了和NSObject類似的引用計數器方法,相關介面放置在CCObject類中。
2.引用計數器——手動管理記憶體
CCObject的及其子類的對象在建立時,引用計數自動化佈建為1。之後每次調用retain,引用計數+1。每次調用release,引用計數-1;若引用計數=0,則直接delete this。
相關介面如下:
複製代碼 代碼如下:
//引用次數+1
virtual void CCObject::retain(void);
//引用次數-1;若引用計數器=0,則delete this;
virtual void CCObject::release(void);
//helper方法,快速判斷當前對象只有唯一引用
bool CCObject::isSingleRefrence(void);
//返回引用次數
unsigned int CCObject::retainCount(void);
原則1:誰產生(new、copy)誰負責release。
例子:
複製代碼 代碼如下:
CCObject *obj=new CCObject;
...
obj->release();
retain是在指標傳遞和賦值時使用的,他的含義是表示擁有。這經常用在指標賦值上。
原則2:誰retain,誰負責release。
例子:
複製代碼 代碼如下:
obj->retain();
...
obj->release();
原則3:傳遞賦值時,需要先retain形參,後release原指標,最後賦值。(注意,因為這裡沒有使用自賦值檢查,所以這組順序不能錯。)
例子:
複製代碼 代碼如下:
void CCNode::setGrid(CCGridBase* pGrid)
{
CC_SAFE_RETAIN(pGrid);
CC_SAFE_RELEASE(m_pGrid);
m_pGrid = pGrid;
}
3.自動釋放池——自動管理記憶體
原則4:對於使用autorelease的對象,不必管它,每幀結束後會自動釋放。
相關介面:
複製代碼 代碼如下:
CCObject* CCObject::autorelease(void);
例子:
複製代碼 代碼如下:
CCObject *obj=new CCOjbect;
obj->autorelease();
...
完全手動管理記憶體,很繁瑣,cocos2d-x提供了自動釋放池CCPoolManager。將對象置於自動釋放池中,每幀繪製結束,就自動release池中的對象。
4.CCNode節點管理
cocos2d-x使用節點群組成一棵樹,渲染的時候要遍曆這棵樹。CCNode是所有節點類的父類,他內部使用了一個CCArray對象管理他的所有子節點,當對象被添加為子節點時,實際上是被添加到CCArray對象中,同時會調用這個對象的retain方法。同理,從CCArray中移除時,也會調用release方法。
相關介面:
複製代碼 代碼如下:
virtual void addChild(CCNode * child);
virtual void addChild(CCNode * child, int zOrder);
virtual void addChild(CCNode * child, int zOrder, int tag);
virtual void removeChild(CCNode* child, bool cleanup);
void removeChildByTag(int tag, bool cleanup);
virtual void removeAllChildrenWithCleanup(bool cleanup);
在切換情境時,系統會遍曆整棵樹的節點,進行release。
5.靜態工廠
cocos2d-x中存在大量的靜態Factory 方法,這些方法中,全都對this指標調用了autorelease函數。如CCSprite中的這些方法:
複製代碼 代碼如下:
static CCSprite* spriteWithTexture(CCTexture2D *pTexture);
static CCSprite* spriteWithTexture(CCTexture2D *pTexture, const CCRect& rect);
static CCSprite* spriteWithTexture(CCTexture2D *pTexture, const CCRect& rect, const CCPoint& offset);
static CCSprite* spriteWithSpriteFrame(CCSpriteFrame *pSpriteFrame);
static CCSprite* spriteWithSpriteFrameName(const char *pszSpriteFrameName);
static CCSprite* spriteWithFile(const char *pszFileName);
static CCSprite* spriteWithFile(const char *pszFileName, const CCRect& rect);
static CCSprite* spriteWithBatchNode(CCSpriteBatchNode *batchNode, const CCRect& rect);
這些方法內部實現了:記憶體配置、初始化、設定autorelease。用靜態工廠來產生對象,可以簡化代碼,是官方建議的方法。
6.cache機制類
cocos2d-x中存在一些cache類,這些都是單例類的管理器。
複製代碼 代碼如下:
CCAnimationCache
CCSpriteFrameCache
CCTextureCache
這些cache內部也使用了ratain和release方法,防止這些資源被釋放掉。
使用這些cache,我們可以儲存預先載入的一些資源,在方便的時候調用它,去綁定給一些對象。注意,這些cache在情境切換時,不會自動刪除,需要手動調用purgeXXXX方法,進行清理。