COCOS2DX引擎深入二————內存管理


我會先用幾句話概括,然後在慢慢深入。

先考慮個問題。

爲什麼要引進內存管理?

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(),那麼就將被回收。



發佈了13 篇原創文章 · 獲贊 0 · 訪問量 3萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章