cocos2dx内存管理是基于引用计数的,有一个Ref类专门用来管理引用计数,所有的cocos2dx对象都是派生自Ref类,从这个类派生的cocos2dx对象可以自动进行内存管理。引用计数为0,就会被释放
1.cocos2dx对象初始化的流程:
auto sp = Sprite::create("CloseNormal.png");
最终会调用Ref的构造函数
Ref::Ref()
: _referenceCount(1) // when the Ref is created, the reference count of it is 1
#if CC_ENABLE_SCRIPT_BINDING
, _luaID (0)
, _scriptObject(nullptr)
, _rooted(false)
, _scriptOwned(false)
,_referenceCountAtRootTime(0)
#endif
{
#if CC_ENABLE_SCRIPT_BINDING
static unsigned int uObjectCount = 0;
_ID = ++uObjectCount;
#endif
#if CC_REF_LEAK_DETECTION
trackRef(this);
#endif
}
创建的时候,会把_referenceCount
置为1。然后看一下Sprite的构造函数:
Sprite* Sprite::create(const std::string& filename)
{
Sprite *sprite = new (std::nothrow) Sprite();
if (sprite && sprite->initWithFile(filename))
{
sprite->autorelease();
return sprite;
}
CC_SAFE_DELETE(sprite);
return nullptr;
}
可以看到autorelease方法,把Sprite加到AutoreleasePool中。然后再Director的mainLoop()
方法中会调用AutoreleasePool的clear()方法。因为mainLoop是每帧调用一次,然后PoolManager::getInstance()->getCurrentPool()->clear();
也会每帧调用一次,看一下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
}
这里会调用obj->release()
方法,会使引用计数-1,如果引用计数为0,会释放这个对象。
到此为止,一个对象创建出来的内存管理流程已经差不多了。
2.添加子节点
rootNode->addChild(sp);
方法是把我们创建出来的cocos2dx对象添加到UI树上,看一下addChild的实现:
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)
{
if (_children.empty())
{
this->childrenAlloc();
}
this->insertChild(child, localZOrder);
if (setTag)
child->setTag(tag);
else
child->setName(name);
child->setParent(this);
child->setOrderOfArrival(s_globalOrderOfArrival++);
if( _running )
{
child->onEnter();
// prevent onEnterTransitionDidFinish to be called twice when a node is added in onEnter
if (_isTransitionFinished)
{
child->onEnterTransitionDidFinish();
}
}
if (_cascadeColorEnabled)
{
updateCascadeColor();
}
if (_cascadeOpacityEnabled)
{
updateCascadeOpacity();
}
}
addChild()方法内部会调用
addChildHelper(),然后重点看一下
this->insertChild(child, localZOrder);`这个方法:
// helper used by reorderChild & add
void Node::insertChild(Node* child, int z)
{
_transformUpdated = true;
_reorderChildDirty = true;
_children.pushBack(child);
child->_localZOrder = z;
}
这里会调用_children.pushBack(child);
这个方法,这里的_children是cocos2dx实现的一个Vector,专门用来存储cocos2dx对象,看一下pushBack
的实现:
void pushBack(T object)
{
CCASSERT(object != nullptr, "The object should not be nullptr");
_data.push_back( object );
object->retain();
}
这里有一个retain操作会对引用计数+1,这样添加节点的时候引用计数就会+1就很明朗了。
3.删除子节点
先看看void Node::removeChild(Node* child, bool cleanup /* = true */)
方法:
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 );
}
注意一下detachChild
这个方法:
void Node::detachChild(Node *child, ssize_t childIndex, bool doCleanup)
{
// IMPORTANT:
// -1st do onExit
// -2nd cleanup
if (_running)
{
child->onExitTransitionDidStart();
child->onExit();
}
// If you don't do cleanup, the child's actions will not get removed and the
// its scheduledSelectors_ dict will not get released!
if (doCleanup)
{
child->cleanup();
}
// set parent nil at the end
child->setParent(nullptr);
_children.erase(childIndex);
}
这里注意一下_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();
return _data.erase(it);
}
这里会调用release()
方法,引用计数—1。到这里,已经完成了,内存管理。
然后看一下child->cleanup()
:
void Node::cleanup()
{
#if CC_ENABLE_SCRIPT_BINDING
if (_scriptType == kScriptTypeJavascript)
{
if (ScriptEngineManager::sendNodeEventToJS(this, kNodeOnCleanup))
return;
}
else if (_scriptType == kScriptTypeLua)
{
ScriptEngineManager::sendNodeEventToLua(this, kNodeOnCleanup);
}
#endif // #if CC_ENABLE_SCRIPT_BINDING
// actions
this->stopAllActions();
this->unscheduleAllCallbacks();
// timers
for( const auto &child: _children)
child->cleanup();
}
主要是一些清理工作,停止动作,取消回调。并对子节点做一个cleanup操作
网友评论