Node still marked as running on node destruction!

250 阅读2分钟

本文已参加[新人创作礼]活动,一起开启掘金创作之路。

今天测试功能的时候,遇到删除节点的时候,出现"Node still marked as running on node destruction! Was base class onExit() called in derived class onExit() implementations?"断言。这边记录一下问题的查找过程以及解决方案。

首先出现这个报错的时候,我们先查找一下源码,发现这个断言是在CCNode.cpp 的析构函数里面抛出来的。 在这里插入图片描述 从代码可以看出,调用析构造函数的时候,_running为True。意思就是要删除的对象当前还是正在运行状态。 出现这种情况很多,下面直接介绍我这边出现的情况。

出现流程: 1、节点A、节点B、节点C分别添加到同一个父节点D上。 2、在节点B的onExit函数里面调用了 A.removeFormParent(); 3、当节点B从父节点移除的时候,就出现了上面的断言。

具体原因如下: 父节点D的子节点列表为 [A、B、C],当调用B.removeFromParent时, 在detachChild函数中,childIndex为1,表示B节点在子节点列表中的第二位。

但是在执行B.onExit的时候,自定义代码执行了A.removeFromParent, 这时节点A移除了,父节点D的子节点列表变为[B,C] 这时候,_children.erase(childIndex);删除第二个元素删除的时节点C, 而C节点仍在运行,所以就抛出断言了。

解决方法: 将detachChild最后的 _children.erase(childIndex); 改为_children.eraseObject(child);

1.节点B的removeFromParent 2.节点B的removeFromParentAndCleanup 3.父节点D的removeChild 4.父节点D的detachChild

4.1 节点B的onExit,自定义代码 A节点的removeFromParent,执行后,父节点D_children只有A和C 4.2 _children.erase(childIndex);

源码参考如下:

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

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


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)
{
    // IMPORTANT:
    //  -1st do onExit
    //  -2nd cleanup
    if (_running)
    {
        child->onExitTransitionDidStart();
        // 如果onExit调用了代码导致_children发生改变,下面就会删除错误
        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();
    }
    
#if CC_ENABLE_GC_FOR_NATIVE_OBJECTS
    auto sEngine = ScriptEngineManager::getInstance()->getScriptEngine();
    if (sEngine)
    {
        sEngine->releaseScriptObject(this, child);
    }
#endif // CC_ENABLE_GC_FOR_NATIVE_OBJECTS
    // set parent nil at the end
    child->setParent(nullptr);
	// 这边不应该用索引删除,应该用值删除
	// 可改成  _children.eraseObject(child);
    _children.erase(childIndex);
}