【深入瞭解cocos2d-x 3.x】如何進行合理的記憶體配置,cocos2d-x3.x
設想如下情境,這是一個典型的記憶體合理分配的情境:在一幀內,有若干個函數,每個函數都會建立一系列的精靈,每個精靈都不同,都會佔用一定的記憶體,精靈的總數可能會有1000個,而一個函數只會建立10個精靈這樣,建立的精靈只會在這個函數中使用,大致代碼如下:
for(int i = 0; i < 10; i++){Sprite* s = Sprite::create();//-- doSomething --}
這樣做會造成記憶體泄露嗎?
答案是當然不會,但是這樣會造成一幀內的記憶體峰值過高,因為在引擎的自動記憶體管理中,所有的釋放記憶體操作都是在每一幀的結束才會進行的,所以就算申請的記憶體在這一幀中不會有其他地方會使用,它的記憶體也不會隨著範圍的結束而釋放的。
那麼我們應該如何最佳化這段代碼呢?如下
AutoreleasePool pool;for (int i = 0; i < 10; i++){Sprite* s = Sprite::create();//-- doSomething --}
只需要在函數的第一句加上
AutoreleasePool pool;
就可以實現在函數結束的時候自動將create的指標釋放了,那麼為什麼會有那麼神奇的效果呢?我們來分析一下這個函數的建構函式以及解構函式,首先分析一下建構函式:
AutoreleasePool::AutoreleasePool(): _name(""){ _managedObjectArray.reserve(150); PoolManager::getInstance()->push(this);}
他向PoolManager的單例中push了自己,我們進入push中看看它的具體實現
void PoolManager::push(AutoreleasePool *pool){ _releasePoolStack.push_back(pool);}
直接向_releasePoolStack棧中壓入了this,那這個將會起到什麼效果呢?這就不得不說一下autorelease的實現了,眾所周知,create函數的記憶體自動管理機制依賴於autorelease函數,那麼autorelease函數是幹嘛用的呢:
Ref* Ref::autorelease(){ PoolManager::getInstance()->getCurrentPool()->addObject(this); return this;}向某個Pool池添加對象,那麼getCurrentPool擷取的是那個記憶體管理池呢?
AutoreleasePool* PoolManager::getCurrentPool() const{ return _releasePoolStack.back();}
就是最後我們通過push添加進來的那個池子,所以每建立一個AutoreleasePool 對象,都會壓入PoolManager中。然後後續的autorelease操作是將對象加入到最新建立的AutoreleasePool 對象中。
那麼最終要通過什麼途徑讓函數在函數結束的時候自動釋放記憶體呢?我們知道,範圍結束的時候,會調用普通對象的解構函式,那麼就來看看AutoreleasePool 的解構函式執行了什麼吧
AutoreleasePool::~AutoreleasePool(){ clear(); PoolManager::getInstance()->pop();}
第一個是clear函數,這是一個非常關鍵的函數,我們跟蹤進去
void AutoreleasePool::clear(){ for (const auto &obj : _managedObjectArray) { obj->release(); } _managedObjectArray.clear();}
它會將所有addObject的對象全部執行一次release操作。這樣就可以實現在函數結束的時候自動釋放記憶體了。
PoolManager::getInstance()->pop();
這一行代碼主要是從PoolManager中將當前AutoreleasePool 對象彈出棧(因為當前對象已經析構了)
上述就是通過使用AutoreleasePool 來合理的管理記憶體了