本文已参加[新人创作礼]活动,一起开启掘金创作之路。
今天测试功能的时候,遇到删除节点的时候,出现"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);
}