转载

cocos2dx - 节点管理

接上一节内容: cocos2dx - v2.3.3编辑器骨骼动画

本节主要Cocos2dx中节点的管理及应用

一般用法

用过Cocos2dx应该都有用过addChild,removeChild方法。或者retain,release方法。

addChild,removeChild方法都是继承Node类,retain,release方法则继承自Ref类。

查看cocos2dx库代码可以看到,Ref中retain,release主要维持了一个 _referenceCount计数变量

通过retain方法可以使 _referenceCount计数加1,同时release可以使 _referenceCount计数减1,并且在 _referenceCount计数为0时,释放对象。

 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;     } } 

查看Node的实现也可以了解Node中维护了一个CCVector对象来存储子节点,在erase子节点时,对子节点调用release方法,同时在pushBack新节点时,对子节点调用retain方法。

下面是CCVector中pushBack及erase的一些细节:

     // Adds objects          /** Adds a new element at the end of the Vector. */     void pushBack(T object)     {         CCASSERT(object != nullptr, "The object should not be nullptr");         _data.push_back( object );         object->retain();     }         /** @brief Removes from the vector with an iterator.       *  @param position Iterator pointing to a single element to be removed from the Vector.      *  @return An iterator pointing to the new location of the element that followed the last element erased by the function call.      *          This is the container end if the operation erased the last element in the sequence.      */     iterator erase(iterator position)     {         CCASSERT(position >= _data.begin() && position < _data.end(), "Invalid position!");         (*position)->release();         return _data.erase(position);     }      

这样就说明了Node节点实际还是在对Ref继承的 _referenceCount 进行计数处理。

若Node没有添加到父节点中,那么_referenceCount什么时候变成0,又什么时候释放节点的?

首先在Ref的构造函数中,可以看到 _referenceCount初始化为1

 Ref::Ref() : _referenceCount(1) // when the Ref is created, the reference count of it is 1 { #if CC_ENABLE_SCRIPT_BINDING     static unsigned int uObjectCount = 0;     _luaID = 0;     _ID = ++uObjectCount;     _scriptObject = nullptr; #endif      #if CC_REF_LEAK_DETECTION     trackRef(this); #endif } 

同时,在Node的静态创建方法Node::create中,宏定义  CREATE_FUNC (Node)  实现如下,

 /** @def CREATE_FUNC(__TYPE__)  * Define a create function for a specific type, such as Layer.  *  * @param __TYPE__  class type to add create(), such as Layer.  */ #define CREATE_FUNC(__TYPE__) / static __TYPE__* create() / { /     __TYPE__ *pRet = new(std::nothrow) __TYPE__(); /     if (pRet && pRet->init()) /     { /         pRet->autorelease(); /         return pRet; /     } /     else /     { /         delete pRet; /         pRet = nullptr; /         return nullptr; /     } / } 

可以看到,在对象创建失败会直接delete,并返回nullptr。创建成功则调用了autorelease方法,如下:

 Ref* Ref::autorelease() {     PoolManager::getInstance()->getCurrentPool()->addObject(this);     return this; } 

说明在正常创建Node节点时,会将其添加到coco2dx的一个内存池进行管理。同时在主循环中看到,下一帧处理对当前内存池对象进行了一次清理,并调用了release方法。

 void DisplayLinkDirector::mainLoop() {     if (_purgeDirectorInNextLoop)     {         _purgeDirectorInNextLoop = false;         purgeDirector();     }     else if (_restartDirectorInNextLoop)     {         _restartDirectorInNextLoop = false;         restartDirector();     }     else if (! _invalid)     {         drawScene();               // release the objects         PoolManager::getInstance()->getCurrentPool()->clear();     } } 
 void AutoreleasePool::clear() { #if defined(COCOS2D_DEBUG) && (COCOS2D_DEBUG > 0)     _isClearing = true; #endif     std::vector<Ref*> releasings;     releasings.swap(_managedObjectArray);     for (const auto &obj : releasings)     {         obj->release();     } #if defined(COCOS2D_DEBUG) && (COCOS2D_DEBUG > 0)     _isClearing = false; #endif } 

因此,如果一个对象在创建后,没有及时进行retain或者添加到父节点中,在当前帧结束时被调用release方法导致 _referenceCount变成0,从而被释放。

几个示例:

 Scene* pScene = Director::getInstance()->getRunningScene(); if (pScene) {     Node* pNode1 =Node::create();     //  pNode1创建后没有处理,在当前帧结束前被释放          Node* pNode2 =Node::create();    //  pNode2创建后添加到场景中,不会被释放     pScene->addChild(pNode2);          Node* pNode3 =pScene->getChildByTag(3);     if(pNode3)     {         pScene->removeChild(pNode3);     //  pNode3被立即释放         pNode2->addChild(pNode3);        // 错误!! pNode3已被释放     }                Node* pNode4 =Node::create();     pNode2->addChild(pNode4);            //  pNode4 随 pNode2释放           Node* pNode5 =Node::create();     pScene->addChild(pNode5);     pNode5->retain();     pScene->removeChild(pNode5);     pNode2->addChild(pNode5);     pNode5->release();                    //  pNode5 随 pNode2释放      } 
正文到此结束
Loading...