歡迎轉載,轉載請註明原文地址: http://blog.csdn.net/majianfei1023/article/details/51025961
本文基於cocos2d-x 3.4
一、cocos2dx內存管理基礎
cocos2dx內存管理是基於引用計數的。使用了基類Ref實現類對象的引用計數記錄。引擎中的所有需要管理的類都派生自Ref。(eg:Node,Sprite,Texture2D…)
首先看一下Ref的結構:
CCRef.h
class CC_DLL Ref
{
public:
void retain();
void release();
Ref* autorelease();
unsigned int getReferenceCount() const;
protected:
Ref();
public:
virtual ~Ref();
protected:
/// count of references
unsigned int _referenceCount;
friend class AutoreleasePool;
};
CCRef.cpp
Ref::Ref()
: _referenceCount(1) // when the Ref is created, the reference count of it is 1
{
}
Ref::~Ref()
{
}
void Ref::retain()
{
CCASSERT(_referenceCount > 0, "reference count should be greater than 0");
++_referenceCount;
}
void Ref::release()
{
CCASSERT(_referenceCount > 0, "reference count should be greater than 0");
--_referenceCount;
if (_referenceCount == 0)
{
delete this;
}
}
Ref* Ref::autorelease()
{
PoolManager::getInstance()->getCurrentPool()->addObject(this);
return this;
}
代碼很簡單,主要就是retain(),release(),autorelease(),_referenceCount和AutoreleasePool。
1.autorelease
爲什麼先看autorelease呢,因爲autorelease是cocos2dx內存管理的根基,就是把對象放入到自動釋放池(AutoreleasePool)中管理。
AutoreleasePool維護了一個vector< Ref*>的數據結構,而autorelease就是把繼承自Ref的的對象放在這個vector裏面管理。
2.retain
retain的作用就是:令其引用計數增1,表示獲取該對象的引用權。我稱之爲認領,如果對象沒有被認領,那麼就會釋放掉。在什麼時候釋放?
每一幀 都會遍歷AutoreleasePool調用clear()
void DisplayLinkDirector::mainLoop()
{
...
else if (! _invalid)
{
drawScene();
// release the objects
PoolManager::getInstance()->getCurrentPool()->clear();
}
}
void AutoreleasePool::clear()
{
for (const auto &obj : _managedObjectArray)
{
obj->release();
}
_managedObjectArray.clear();
}
retain():令其引用計數增1,表示獲取該對象的引用權。也就是認領對象,如果對象沒有被認領,那麼就會
3.release()
要真正釋放的時候調用,無論是主動調用去釋放,還是代碼中自動遍歷去釋放,都需要經過這個函數,令其引用計數值減1,表示釋放該對象的引用權。當引用計數爲0的時候,就真正釋放對象。
4.AutoreleasePool
管理自動釋放的對象。(當釋放池自身被釋放的時候,它就會對池中的所有對象執行一次release()方法)
也就是說,如果對象沒有被認領(比如addChild,我們稍後討論),那麼在每一幀drawScene之後,調用AutoreleasePool.clear()->Ref.release(),減少引用計數,然後就會釋放掉沒被認領的對象,如果被認領呢?那就只是把創建的時候的引用計數給減掉,然後由調用者或者引擎其他管理者管理。
AutoreleasePool的vector每次只管理一幀內創建的對象,然後drawScene()之後會clear掉。
也就是說,每一次vector裏面只是存了從上一幀的_managedObjectArray.clear() 到下一幀的 _managedObjectArray.clear() 之間創建的對象。
那麼被認領的對象,什麼時候釋放呢?我們來看看引擎中怎麼用的。
二、引擎用法
1.addChild
Node裏面有一個addChild函數,addChild會調用retain來認領對象,你是不是沒有找到,反正我最初怎麼也找不到
void Node::insertChild(Node* child, int z)
{
_transformUpdated = true;
_reorderChildDirty = true;
_children.pushBack(child);
child->_localZOrder = z;
}
void pushBack(T object)
{
CCASSERT(object != nullptr, "The object should not be nullptr");
_data.push_back( object );
object->retain();
}
pushBack!!!
我還一直是標準的vector,原來是自定義的,被騙了那麼久。
那麼釋放的時候就簡單了
removeChild裏面有一個_children.erase(childIndex);(當然不只一個接口,還有類似的地方)
實際上就是調用(*iter)->release();去釋放了。
2.addImage
CCTextureCache裏面有一個重載函數
Texture2D* addImage(const std::string &filepath);
Texture2D* addImage(Image *image, const std::string &key);
實現裏面有一點不同,下面那個接口使用的時候
_textures.insert( std::make_pair(key, texture) );
texture->retain();
texture->autorelease();
調用了retain和autorelease,而上面的接口並沒有調用這兩行代碼,當時百思不得其解,現在看來,也只是放不放到AutoreleasePool而已,對它的釋放來講,並沒有什麼區別。因爲它的釋放是在
void TextureCache::removeAllTextures()
{
for( auto it=_textures.begin(); it!=_textures.end(); ++it ) {
(it->second)->release();
}
_textures.clear();
}
在這個以及相關的函數 eg:removeUnusedTextures,removeTexture… 裏面釋放的。
三、結論
AutoreleasePool有什麼用?簡單來說,沒什麼用。但是引擎底層是這麼寫的,你又必須按着它的寫法來,不然很可能報錯。
好吧,還是有用的,它的用途就是防止你手動create了一個對象,但是,沒有任何地方引用,也沒有任何地方釋放,這個時候AutoreleasePool就發揮作用了,他會幫你釋放。前提是你調用了autorelease(),(create幫我們做了)。