cocos2dx内存管理

2017-10-12  本文已影响123人  咸鱼而已

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操作

上一篇下一篇

猜你喜欢

热点阅读