2

我在 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 应该为我完成所有的工作。我在这里假设一些根本错误的事情吗?是否严格禁止不同场景之间的物体重复使用?

4

3 回答 3

4

正如其他人已经指出的那样,一个节点只能有一个父节点。让我指出实际的误解。这是您的代码:

CCScene *scene1 =  [HelloWorldLayer scene];
CCScene *scene2 =  [HelloWorldLayer2 scene];

您创建了两个对象scene1 和scene2。它们将保留在范围内,直到当前方法结束。这是您问题的关键。

[director_ pushScene: scene1];
[[scene1 getChildByTag:1] addChild:label];

从 cocos2d 的角度来看,scene1 对象现在是活动场景。您已将标签对象添加为子对象。

[director_ replaceScene: scene2];

scene2 对象不是 cocos2d 中的活动场景。但是,scene1 对象仍然在范围内(当前方法的本地范围),因此直到当前代码块退出(即方法返回)后才会释放它。

[[scene2 getChildByTag:2] addChild:label];

在这里,您尝试将标签作为子节点添加到已经是场景 1 中的子节点的场景 2。砰!并且有充分的理由。

假设您使用的是 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 的自动释放工作方式,在 replaceScene 之后,scene1 对象可能会继续存在。最迟会在下一帧开始之前释放 scene1 对象。

于 2013-01-16T22:35:25.527 回答
2

cocos 对象只能有一个父对象。当您尝试将其添加到场景 2 中时,“标签”对象已经有一个父对象(场景 1 的标签为 1 的子对象)。如果这是您的意图,您应该为场景 2 创建标签对象的另一个实例。

于 2013-01-16T20:02:30.163 回答
0

这里实际发生的是它scene1没有被释放,因为它仍然被 ARC 保留。

孩子的父母还是给nil里面的CCNode dealloc方法。如果场景永远不会被释放,那么孩子的父母仍然是场景1,它很高兴存在。

当您将标签添加到 scene2 时,您会因child.parent == nil.

这不是 cocos2d 或 ARC 的错误。正是您编写代码的方式导致了这种情况。像这样重用 CCNode 通常不是一个好主意。

于 2013-01-16T21:34:03.640 回答