【深入瞭解cocos2d-x 3.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 來合理的管理內存了

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章