cocos2dx报错 node still marked as running~另类原因

Assert failed: Node still marked as running on node destruction! Was base class onExit() called in derived class onExit() implementations?

在写代码时报了这个错误,主要做的事是监听一个node,在其移除的时候调用另一个node的removeFromParent,实现关联移除。
网上去查,普遍是说原因是一个node重载了Node类的onExit方法却没调用父类onExit方法,解决方法为删掉自己写的onExit或者在其中调用Node类的onExit方法。
因为自己用的是lua,水平也有限,不会重载更不会调用Node类的onExit,觉得似乎问题不在这里。。。但网上找的解决方法几乎都是如此。。。


于是乎,尝试第一次查看cocos源码:

首先,这个是Node类的析构函数里的一句:

Node::~Node()
{
    //...sth
    CCASSERT(!_running, "Node still marked as running on node destruction! Was base class onExit() called in derived class onExit() implementations?");
}

然后我们去找这个_running在哪里被赋值:

void Node::onEnter()
{
    //...sth
    _running = true;
}
...

void Node::onExit()
{
    //...sth
    _running = false;
}

可以看得出来,node在生成的时候调用onEnter此时_running = true, 而退出的时候调用onExit此时_running = false。
貌似还没找到关键,查看报错,发现是removeFromParent的时候报的错,从这里看起:

void Node::removeFromParent()
{
    this->removeFromParentAndCleanup();
}

void Node::removeFromParentAndCleanup(bool cleanup)
{
    if(_parent != nullptr)
    {
        _parent->removeChild(this, cleanup)
    }
}

看得出来,子节点移除也就是用父节点执行removeChild罢了。继续看下去:

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

    //...sth

    // set parent nil at the end
    child->setParent(nullptr);

    _children.erase(childIndex);
}

这里发现一个节点在移除子节点的时候会先调用子节点的onExit方法进行移除前的一些操作,嗯,跟我们想的差不多。等下~!这里发现要调用子节点的oneExit是有条件的:if (_running)
又是_running
也就是说,我们的报错很可能是没有进入这个判断之中,没有调用被移除节点的onExit导致的!
而这个条件没进去,代表父节点的_running = false。结合上面的发现,说明父节点已经调用了onExit,这里不允许父节点在onExit之后还进行removeChild操作。


嗯,条理已经很清晰了。现在回想下我代码的逻辑:

local nodeParent = cc.Node.create()
local nodeChild1 = cc.Node.create()
local nodeChild2 = cc.Node.create()
nodeParent:addChild(nodeChild1)
nodeParent:addChild(nodeChild2)

nodeChild1:registerScripterHandler(function(event)
    if event == "exit" then
        nodeChild2:removeFromParent()
    end
end)

差不多是这样子,只是实际上我的”nodeParent”是一个scene,而查看代码发现scene其实也是一个Node。
上述代码会在nodeParent移除的时候报错,报错位置在nodeChild2:removeFromParent()
根据上述分析,原因显而易见了,child1根child2拥有同一个祖先nodeParent。而要实现的逻辑是:child1移除的时候自动移除child2。但child1移除的情况多是因为parent关闭了,级联受到移除,而此时parent已经调用了自己的onExit的,也就是_running = fasle, 这个时候child2受到child1的级联移除(自己写的逻辑),要调用parent的detachChild,而其中这条语句:

    if (_running)
    {
        child->onExitTransitionDidStart();
        child->onExit();
    }

此时parent的_running = false, 是进不去这个判断语句的,也就无法调用child2的onExit方法,也就导致了child2在析构时报错~!!


真相大白!可是知道了原因之后,我还是要写这个逻辑啊!怎么办呢?查了一下cocos的api,发现Node的一个方法可以查询一个节点的_running状态。我们只需要这样做:

nodeChild1:registerScripterHandler(function(event)
    if event == "exit" then
        if nodeChild2:getParent():isRunning() == true then
            nodeChild2:removeFromParent()
        else
            print("nodeChild2的父节点已处于停止运行状态,nodeChild2接下来会被自动移除,不需要重复移除!")
        end
    end
end)

因为可以看得出,parent已经正在移除状态中,因此这里可以跳过child2的移除,这样后面child2也会被parent级联移除~~~


另外,源码查看方面受这篇文章启发~cocos2dx 3.x内存管理源代码追踪

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值