我在Cocos2d的一个项目中痛苦不堪。我创建了一个小项目,隔离了我的“误解”的核心。
以下是一个非常简单的代码,它创建了两个独立的场景,并假装重用第一个的子代。我在使用ARC的项目中使用cocos2d v2。
CCLabelTTF *label = [CCLabelTTF labelWithString:@"Hello ARC World" fontName:@"Marker Felt" fontSize:64];
CCScene *scene1 = [HelloWorldLayer scene];
CCScene *scene2 = [HelloWorldLayer2 scene];
[director_ pushScene: scene1];
// I add my label to the main layer of my scene1.
[[scene1 getChildByTag:1] addChild:label];
//I reset my own scene1 pointer to make sure only the director points to it.
//scene1 = nil;
// I replace the scene, and hope that the old scene1 will be killed by cocos
[director_ replaceScene: scene2];
// When I want to reuse my "label" object, I get the "child already added..." exception
[[scene2 getChildByTag:2] addChild:label];
为什么这是错的? 我已经读过我不应该和RemoveAllChildren一样混乱,因为replaceScene应该为我做所有的工作.. 我在这里假设一些根本错误的东西吗?是否严格禁止在不同场景之间重用对象?
答案 0 :(得分:4)
正如其他人已经指出的那样,一个节点只能有一个父节点。让我指出你实际的误解。这是你的代码:
CCScene *scene1 = [HelloWorldLayer scene];
CCScene *scene2 = [HelloWorldLayer2 scene];
您创建了两个对象scene1和scene2。它们将保持在范围内,直到当前方法结束。这是您问题的关键。
[director_ pushScene: scene1];
[[scene1 getChildByTag:1] addChild:label];
现在,scene1对象是cocos2d视角下的活动场景。您已将标签对象添加为子项。
[director_ replaceScene: scene2];
scene2对象不是cocos2d中的活动场景。但是scene1对象仍在范围内(当前方法的局部范围),因此在当前代码块退出之后(即方法返回),它才会被释放。
[[scene2 getChildByTag:2] addChild:label];
在这里,您尝试将标签作为子节点添加到scene2,它已经是scene1中的子节点。 BAMM!并且有充分的理由。
您可能想尝试以下操作,假设您正在使用ARC,这应该可行:
{
CCScene *scene1 = [HelloWorldLayer scene];
[director_ pushScene: scene1];
[[scene1 getChildByTag:1] addChild:label];
}
{
CCScene *scene2 = [HelloWorldLayer2 scene];
[director_ replaceScene: scene2];
[[scene2 getChildByTag:2] addChild:label];
}
通过添加额外的代码块,运行replaceScene时应释放scene1对象。为什么?因为scene1仅在第一个代码块的范围内,所以在第二个代码块中,scene1变量已经超出范围,ARC知道它可以从内存中释放,假设cocos2d在replaceScene方法期间也删除了对scene1的所有强引用。
这最后一个假设是我不确定的,因为cocos2d本身不使用ARC,所以由于cocos2d的autorelease工作方式,scene1对象可能会在replaceScene之后继续存在。最迟,scene1对象将在下一帧开始之前释放。
答案 1 :(得分:2)
cocos对象只能有一个父对象。当您尝试在scene2中添加它时,'label'对象已经有一个父(scene1的子标签为1)。如果这是你的意图,你应该为scene2创建另一个标签对象实例。
答案 2 :(得分:0)
这里实际发生的是scene1
没有被释放,因为它仍然被ARC保留。
儿童的父母仍然在nil
方法内CCNode dealloc
。如果场景永远不会被dealloc'd,那么孩子的父母仍然是scene1,幸福地存在。
当你去向scene2添加标签时,你正在绊倒child.parent == nil
的断言。
这不是cocos2d或ARC的错误。这就是编写导致此问题的代码的方式。重复使用CCNode通常不是一个好主意。