cocos2dx 内存管理

前端之家收集整理的这篇文章主要介绍了cocos2dx 内存管理前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

欢迎转载,转载请注明原文地址: http://www.jb51.cc/article/p-oggvgroc-hh.html

本文基于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帮我们做了)。

猜你在找的Cocos2d-x相关文章