cocos2dx 內存管理

歡迎轉載,轉載請註明原文地址: 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幫我們做了)。

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