在 removeFromParent 上 Sprite Kit iOS 7.1 崩溃

Posted

技术标签:

【中文标题】在 removeFromParent 上 Sprite Kit iOS 7.1 崩溃【英文标题】:Sprite Kit iOS 7.1 crash on removeFromParent 【发布时间】:2014-04-19 09:45:26 【问题描述】:

我已将 iPad Air 更新到 7.1,将 Xcode 更新到 5.1。 Xcode 想将我的项目更新为推荐的设置,我同意了。

之后,当我从父级中删除节点时,我的游戏开始在几个地方崩溃。

这对我来说是一个惊喜——更新之前没有过境点。我恢复了我的项目,发现 Xcode 对它做了什么——只改变了 Architectures 字符串:

之前:

之后:

在旧版本中没有崩溃。 如果我在新版本中删除 arm64 支持,则不会发生崩溃。 在模拟器中新旧版本都没有崩溃。

我应该在我的代码中注意哪些地方?

代码栈:

SpriteKit`SKCSprite::removeSubsprite(SKCSprite*):
0x1859442cc:  stp    fp, lr, [sp, #-16]!
0x1859442d0:  add    fp, sp, 0
0x1859442d4:  stp    x20, x19, [sp, #-16]!
0x1859442d8:  sub    sp, sp, #16
0x1859442dc:  mov    x19, x0
0x1859442e0:  str    x1, [sp, #8]
0x1859442e4:  add    x20, sp, 8
0x1859442e8:  add    x0, x19, 544
0x1859442ec:  mov    x1, x20
0x1859442f0:  bl     0x18594872c               ; unsigned long std::__1::__tree<SKCSprite*, std::__1::less<SKCSprite*>, std::__1::allocator<SKCSprite*> >::__erase_unique<SKCSprite*>(SKCSprite* const&)
0x1859442f4:  add    x0, x19, 464
0x1859442f8:  mov    x1, x20
0x1859442fc:  bl     0x185948218               ; std::__1::list<SKCSprite*, std::__1::allocator<SKCSprite*> >::remove(SKCSprite* const&)
0x185944300:  ldr    x20, [sp, 1]
0x185944304:  ldrb   w8, [x20, #18]
0x185944308:  ldrh   w9, [x20, #16]
0x18594430c:  orr    w8, w9, w8, lsl #16
0x185944310:  tbnz   w8, #1, 0x185944324       ; SKCSprite::removeSubsprite(SKCSprite*) + 88
0x185944314:  ldr    x9, [x20, 61]
0x185944318:  ldr    x9, [x9, 2]
0x18594431c:  cbnz   x9, 0x185944324           ; SKCSprite::removeSubsprite(SKCSprite*) + 88
0x185944320:  tbz    w8, #8, 0x185944330       ; SKCSprite::removeSubsprite(SKCSprite*) + 100
0x185944324:  mov    x0, x19
0x185944328:  mov    x1, x20
0x18594432c:  bl     0x18594828c               ; SKCSprite::removeFromOffscreenList(SKCSprite*)
0x185944330:  str    xzr, [x20, #392]
0x185944334:  ldr    x8, [x20, 0]
0x185944338:  ldr    x8, [x8, 10]
0x18594433c:  mov    x0, x20
0x185944340:  blr    x8
0x185944344:  ldrh   w8, [x19, #20]
0x185944348:  orr    w9, w8, #0x40
0x18594434c:  strh   w9, [x19, #20]
0x185944350:  ldr    x8, [x19, 49]
0x185944354:  cbz    x8, 0x185944388           ; SKCSprite::removeSubsprite(SKCSprite*) + 188
0x185944358:  add    x9, x19, 392
0x18594435c:  ldrh   w10, [x8, #20]
0x185944360:  orr    w10, w10, #0x40
0x185944364:  strh   w10, [x8, #20]
0x185944368:  ldr    x8, [x9, 0]
0x18594436c:  add    x9, x8, 392
0x185944370:  ldrh   w10, [x8, #20]
0x185944374:  orr    w10, w10, #0x40
0x185944378:  strh   w10, [x8, #20]
0x18594437c:  ldr    x8, [x8, 49]
0x185944380:  cbnz   x8, 0x18594435c           ; SKCSprite::removeSubsprite(SKCSprite*) + 144
<b>0x185944384:  ldrh   w9, [x19, #20]  EXC_BAD_ACCESS here.</b>
0x185944388:  orr    w8, w9, #0x2
0x18594438c:  strh   w8, [x19, #20]
0x185944390:  b      0x185944398               ; SKCSprite::removeSubsprite(SKCSprite*) + 204
0x185944394:  ldrh   w8, [x19, #20]
0x185944398:  tbnz   w8, #7, 0x1859443ac       ; SKCSprite::removeSubsprite(SKCSprite*) + 224
0x18594439c:  orr    w8, w8, #0x80
0x1859443a0:  strh   w8, [x19, #20]
0x1859443a4:  ldr    x19, [x19, 49]
0x1859443a8:  cbnz   x19, 0x185944394          ; SKCSprite::removeSubsprite(SKCSprite*) + 200
0x1859443ac:  sub    sp, fp, #16
0x1859443b0:  ldp    x20, x19, [sp], #16
0x1859443b4:  ldp    fp, lr, [sp], #16
0x1859443b8:  ret    lr

更新:新信息:

我的错误:

堆栈:

我发现有问题。我要删除的节点包含两个 SKSpriteNode(我的精灵图像及其阴影)和一个 SKShapeNode(一根细黑绳)。我尝试用 SKSpriteNode 绳索替换我的 SKShapeNode 绳索,我取得了成功——问题消失了。

从父节点中删除我的节点的代码:

SKAction *moveCloud = [SKAction moveToX:destinationX duration:moveDuration];
[cloud runAction:moveCloud completion:^
    [cloud removeFromParent];
];

向我的节点添加 SKShapeNode 绳索的代码:

- (void)addRopeToCloud:(SKNode *)cloud

    CGFloat maxY = self.scene.size.height;
    CGFloat length = maxY - [self.scene convertPoint:cloud.position fromNode:cloud.parent].y;

    CGMutablePathRef path = CGPathCreateMutable();
    CGPathMoveToPoint(path, NULL, 0, 0);
    CGPathAddLineToPoint(path, NULL, 0, length);

    SKShapeNode *rope = [[SKShapeNode alloc] init];
    rope.path = path;
    rope.strokeColor = [UIColor blackColor];
    rope.lineWidth = 0.1;
    rope.antialiased = YES;
    rope.zPosition = -0.01;

    CGFloat threadScale = 1 / cloud.xScale;

    rope.xScale = threadScale;
    rope.yScale = threadScale;

    [cloud addChild:rope];
    CGPathRelease(path);

向我的节点添加 SKSpriteNode 绳索的代码,可以解决问题:

- (void)addRopeToCloud:(SKNode *)cloud

    CGFloat maxY = self.scene.size.height;
    CGFloat length = maxY - [self.scene convertPoint:cloud.position fromNode:cloud.parent].y;

    CGSize ropeSize = CGSizeMake(1.5, length);
    SKSpriteNode *rope = [SKSpriteNode spriteNodeWithColor:[SKColor blackColor] size:ropeSize];
    rope.anchorPoint = CGPointMake(0.5, 0);

    CGFloat ropeScale = 1 / cloud.xScale;
    rope.xScale = ropeScale;
    rope.yScale = ropeScale;

    rope.zPosition = -0.01;

    [cloud addChild:rope];

我的 SKShapeNode 添加代码似乎有问题,但我不明白到底是什么。

【问题讨论】:

你能发布调用堆栈+启动从父级删除的代码/方法吗?还要发布实际的错误消息。如果您还没有添加异常断点。 你已经用你的标题说你的游戏会在 removeFromParent 上崩溃。那你还需要知道什么? FWIW 删除带有 SKShapeNode 子节点的节点时,我遇到了类似/相同的崩溃。当我启用向每个节点添加 SKShapeNode 的调试绘图时,总是会发生这种情况。这看起来像一个错误。 我已经挣扎了几个晚上,试图了解发生了什么以及为什么我得到 EXC_BAD_ACCESS,我正在尝试一切并深入研究 SKCSprite::removeSubsprite - 现在在看到这篇文章之后,至少我会今晚睡个好觉 这可能是 Sprite Kit 中的一个错误。 Have you filed it with Apple? 【参考方案1】:

这似乎只发生在 ios 7.1 上。不确定这是否是 Apple 错误,但这似乎可以解决它:

- (void)removeFromParent

    [self.aShapeNode removeFromParent];
    self.aShapeNode = nil;

    [super removeFromParent];

【讨论】:

是的!这段代码应该可以工作。但是你需要继承 SKNode 来覆盖 removeFromParent。 你在 SKNode 子类中这样做吗?我不明白 self.aShapeNode 在子类中的含义。 这是如何在子类 ShapeNode 中工作的?对我来说是: - (void)removeFromParent [self removeFromParent];自我=无; [超级removeFromParent]; 实施了这项技术,但它的崩溃问题并没有消失。我现在必须禁用我的剔除代码。【参考方案2】:

您可以在释放(或呈现新场景)SKShapeNodes 之前调用此方法

- (void)cleanUpChildrenAndRemove:(SKNode*)node 
    for (SKNode *child in node.children) 
        [self cleanUpChildrenAndRemove:child];
    
    [node removeFromParent];

我有同样的问题,但解决了它。我原来的问题:

SKShapeNode producing crash sometimes on dealloc EXC_BAD_ACCESS

【讨论】:

【参考方案3】:

当 SKShapeNode 在 iOS 7.1 中从父视图中移除时,我也会崩溃

我的解决方法是在将 SKShapeNode 属性从父级移除之前将其设置为 nil

【讨论】:

你先从父节点中删除你的 SKShapeNode 吗? 不,只是在其父调用 removeFromParent ex 之前将其设置为 nil。 -(void)removeFromParent self.myShapeNode = nil; [超级removeFromParent]; 无论如何,这是苹果的错误,这只是解决方法。 @saranpol 我不明白如果 self.myShapeNode 在 SKShapeNode 子类中会是什么? 就我而言,self 是 SKSpriteNode 子类,我将 myShapeNode (SKShapeNode) 作为属性和子类。关键是我们只需要在它从场景中删除父级时自动从父级删除之前解除分配(设置为零)SKShapeNode。【参考方案4】:

不确定我的情况是否完全模仿你的情况,但我遇到了同样的错误(具有相同的堆栈跟踪)并意识到我已经设置了两个类,每个类都将 SKShapeNode 对象作为属性。我很确定当我调用removeFromParent 以删除ClassA 中的对象node 时,该对象已被释放。然后,在ClassB 中,我调用了self.node = aNewNode(请记住self.node 指向的对象已被释放),自动合成的setter 第二次尝试释放node

我认为 ARC 应该跟踪所有这些,但错误非常零星,所以老实说,我不能 100% 确定发生了什么。我有一个具有相同模式的SKSpriteNode,但我从未见过它导致此错误。我现在的解决方法是使ClassB 属性weak,所以如果self.node 已经被释放,这不是问题。希望有帮助!

【讨论】:

这对我有帮助。我能够解决我的问题。我的 SKShapeNode 在另一个类中有一个 __weak 引用。我删除了 __weak 属性,崩溃停止了。【参考方案5】:

我也遇到了崩溃。但是,覆盖 removeFromParent 方法并将 SKShapeNode 设置为 nil 的解决方案对我不起作用。

我确实发现我的崩溃是以下因素的组合:

    拥有 SKShapeNode(SKNode 的子类或子类)

    将 SKPhysicsNode 附加到节点(附加到父 SKNode 或 SKShapeNode)。

    调用方法removeFromParent

我最终使用的解决方案是使用以下内容覆盖我的 SKShapeNode 子类中的 removeFromParent 方法:

- (void)removeFromParent

    if( self.physicsBody != nil )
    
        self.physicsBody = nil;
    

    [super removeFromParent];

【讨论】:

以上是关于在 removeFromParent 上 Sprite Kit iOS 7.1 崩溃的主要内容,如果未能解决你的问题,请参考以下文章

removeFromParent() 奇怪的行为

removeFromParent()奇怪的行为

如何在 Spritekit 中的 removeFromParent 之后再次开始操作?

SKNode 上的 removeFromParent 是不是会破坏实例?

在Spritekit中,如何在removeFromParent之后再次启动动作?

quick3.5 removeFromParent()导致的windows下模拟器崩溃问题