如何避免在使用ARC的Cocos2d中“已经添加了孩子。它无法再添加”?

时间:2013-01-16 19:57:51

标签: iphone objective-c cocos2d-iphone

我在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应该为我做所有的工作.. 我在这里假设一些根本错误的东西吗?是否严格禁止在不同场景之间重用对象?

3 个答案:

答案 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通常不是一个好主意。