先考慮個問題。
爲什麼要引進內存管理?
c++不像java,c#有自己的垃圾回收機制。所以很容易出現用完的內存空間沒有被回收,即內存泄露。所以要引進內存管理。
cocos2dx用到的內存管理技術是引用技術。即當對象增加一次引用時,計數器加一;減少一時,計數器減一。分別對應引用計數類Ref的retain()和rrelease();
class CC_DLL Ref
{
public:
/**
* 增加一次引用計數
*/
void retain();
/**
* 釋放一次計數,具體裏面怎麼釋放的,下面我們跟定義的代碼做分析
*/
void release();
/**
* 自動釋放
*/
Ref* autorelease();
/**
* 得到當前對象的引用計數的值,也是是被引用了多少次
*/
unsigned int getReferenceCount() const;
protected:
/**
* Constructor
*
* The Ref's reference count is 1 after construction.
* @js NA
*/
Ref();
public:
/**
* @js NA
* @lua NA
*/
virtual ~Ref();
protected:
/// 用來記錄引用次數的變量值
unsigned int _referenceCount;
friend class AutoreleasePool;
#if CC_ENABLE_SCRIPT_BINDING
public:
/// 對象的ID
unsigned int _ID;
/// 這個變量這裏猜測是lua腳本中引用的ID
int _luaID;
#endif
};
凡是繼承了Ref的類都是自動釋放類,這些類每次創建完之後,引用都爲1。看到Ref有個autorelease()和一個友元類AutoreleasePool,這是內存管理用到的一個很重要的類。
那他們是幹嘛的呢?
我們知道類創建完後,如果沒被用到或不用了,內存是需要被回收的,否則就會出現內存泄露的情況。cocos2dx每一幀結束都會進行內存回收。
怎麼回收呢?
先把調用了autorelease的對象加入到AutoreleasePool這個回收池裏面,每一幀結束後將AutoreleasePool裏的所有對象進行一次release(),並清空。
Ref* Ref::autorelease()
{
PoolManager::getInstance()->getCurrentPool()->addObject(this);
return this;
}
void DisplayLinkDirector::mainLoop()
{
if (_purgeDirectorInNextLoop)
{
_purgeDirectorInNextLoop = false;
purgeDirector();
}
else if (! _invalid)
{
drawScene();
// release the objects
PoolManager::getInstance()->getCurrentPool()->clear();
}
}
void AutoreleasePool::clear()
{
#if defined(COCOS2D_DEBUG) && (COCOS2D_DEBUG > 0)
_isClearing = true;
#endif
for (const auto &obj : _managedObjectArray)
{
obj->release();
}
_managedObjectArray.clear();
#if defined(COCOS2D_DEBUG) && (COCOS2D_DEBUG > 0)
_isClearing = false;
#endif
}
void Ref::release()
{
CCASSERT(_referenceCount > 0, "reference count should greater than 0");
--_referenceCount;
if (_referenceCount == 0)
{
#if defined(COCOS2D_DEBUG) && (COCOS2D_DEBUG > 0)
auto poolManager = PoolManager::getInstance();
if (!poolManager->getCurrentPool()->isClearing() && poolManager->isObjectInPools(this))
{
CCASSERT(false, "The reference shouldn't be 0 because it is still in autorelease pool.");
}
#endif
delete this;
}
}
對象被創建的時候引用爲1,如果在一幀結束前,對象被其他類用到了,然後該對象調用了retain(),引用爲2,那麼AutoreleasePool釋放的時候調用release()後,引用爲1,則不會回收;
如果在一幀結束前,一直未調用retain(),那麼就將被回收。