cocos2d-x 3.6版本学习笔记-内存管理之Node对象

来源:互联网 发布:环球捕手 知乎 编辑:程序博客网 时间:2024/06/10 16:52

由于C++目前未提供完整的垃圾回收机制,cocos2d-x必须手动来管理内存。3.6版本里,cocos2d-x使用了一种类oc的内存管理机制,利用引用计算来控制对象的释放。

在cocos2d-x中,几乎所有的对象都继承自Ref类,而各对象的引用计数也都在Ref类中维护,Ref类结构如下(代码1):

class CC_DLL Ref{public:    void retain();    void release();    Ref* autorelease();    unsigned int getReferenceCount() const;protected:    unsigned int _referenceCount;    friend class AutoreleasePool;    ...};
对象每retain一次引用计数加1,每release一次引用计数减1,当引用计算减到0时,释放资源。
我们可以手动retain,release控制对象的引用计数,也可以通过autorelease来自动化管理对象引用计数。

所谓的自动化管理,就是调用create方法创建Ref对象后,无需手动调用release方法,系统在适当的时候自动调用release方法,这种方便主要是针对 node类型的对象。

以Sprite为例,首先创建一个Sprite对象,create函数实现如下(代码2):

Sprite* Sprite::create(const std::string& filename){    Sprite *sprite = new (std::nothrow) Sprite();//所有Ref对象引用计数,初始值为1    if (sprite && sprite->initWithFile(filename))    {        sprite->autorelease();        return sprite;    }    CC_SAFE_DELETE(sprite);    return nullptr;}
成功的话,返回一个autorelease对象,autorelease函数实现如下(代码3):

Ref* Ref::autorelease(){    PoolManager::getInstance()->getCurrentPool()->addObject(this);    return this;}
void AutoreleasePool::addObject(Ref* object){    _managedObjectArray.push_back(object);}
autorelease只是将对象添加到一个全局的对象池中。其中_managedObjectArray是一个std::vector<Ref*>类型的容器(注,是标准库的容器,非cocos容器)。
至此,对象创建完成。

第二步,将sprite添加到helloworld场景当中。代码如下(代码4):

bool HelloWorld::init(){    ...        this->addChild(sprite, 0);        return true;}
void Node::addChild(Node *child, int zOrder){    CCASSERT( child != nullptr, "Argument must be non-nil");    this->addChild(child, zOrder, child->_name);}
void Node::addChild(Node* child, int localZOrder, const std::string &name){    CCASSERT(child != nullptr, "Argument must be non-nil");    CCASSERT(child->_parent == nullptr, "child already added. It can't be added again");        addChildHelper(child, localZOrder, INVALID_TAG, name, false);}
void Node::addChildHelper(Node* child, int localZOrder, int tag, const std::string &name, bool setTag){    ...    this->insertChild(child, localZOrder);    ...}
void Node::insertChild(Node* child, int z){    _transformUpdated = true;    _reorderChildDirty = true;    _children.pushBack(child);//注:这里的_children窗口为cocos2d::Vector<Node*>类型,非std::Vector    child->_localZOrder = z;}
void pushBack(T object)    {        CCASSERT(object != nullptr, "The object should not be nullptr");        _data.push_back( object );        object->retain();//引用计数为2    }
可以看到,在sprite添加到父节点时,子结点会有一次retain()的操作,retain()完后引用计数为2。

在一帧绘制完毕后,director将会对对象池里的对象进行一次release,代码如下(代码5):

void DisplayLinkDirector::mainLoop(){        ...        drawScene();//sprite引用计数为2             PoolManager::getInstance()->getCurrentPool()->clear();//sprite引用计数为1        ...}
clear函数中,将对对象池中的所有对象引用计算减1。

可以看到,上述过程中,sprite对象引用计数一直大于0,所以一直未销毁(程序的逻辑上确实也是如此)。

那么问题来了,sprite什么时候销毁呢?当引用计数减到0的时候(这是废话),有几种情况会使的sprite的引用计数减1:

1)场景销毁的时候_runningScene->release();

2)场景重新运行的时候

3)sprite从父节点remove掉的时候

前2种情况是对所有的对象池里的数据进行引用减1,第3种是对特定的remove对象引用减1。代码如下(代码6):

void Node::removeChild(Node* child, bool cleanup /* = true */){    // explicit nil handling    if (_children.empty())    {        return;    }    ssize_t index = _children.getIndex(child);    if( index != CC_INVALID_INDEX )        this->detachChild( child, index, cleanup );}void Node::detachChild(Node *child, ssize_t childIndex, bool doCleanup){    ...    // set parent nil at the end    child->setParent(nullptr);    _children.erase(childIndex);}iterator erase(ssize_t index)    {        CCASSERT(!_data.empty() && index >=0 && index < size(), "Invalid index!");        auto it = std::next( begin(), index );        (*it)->release();//减1操作在此!        return _data.erase(it);    }
当对象引用计数为0的时候,对象会释放自身的资源,并且会删除对象池里的引用(如果存在的话)。

总结一下:cocos的autorelease(自动释放)机制,即:

对于node的子类,将new与delete操作与create(),addchild(),removechild()等操作绑定。 当对象从UI上移除也表示着其资源得到释放。

0 0