运行 SKScene 的子节点上的一个简单操作导致 EXC_BAD_ACCESS iOS 7.1

Running a simple action on the childnode of an SKScene causes EXC_BAD_ACCESS in iOS 7.1

运行 在对我一直在开发的 SpriteKit 游戏进行最后润色时克服了这个 运行 时间错误。在 iOS 8 上运行良好,但在 iOS 7.1(模拟器和设备)上崩溃。

我创建了一个小项目,它重现了我正在处理的问题。它通过解压缩在 Xcode 6 中提供的编辑器中创建的 sks 文件来创建 GameScene(就像创建新的 SpriteKit 项目时生成的示例项目一样)。使用 childNodeWithName: 在生成的场景中获取对 sprite 的引用后,尝试 运行 对其进行简单操作会使程序崩溃并引发 EXC_BAD_ACCESS。直接修改精灵的属性似乎工作正常(见代码)。

-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    /* Called when a touch begins */

    for (UITouch *touch in touches) {
        CGPoint location = [touch locationInNode:self];

        SKSpriteNode *sprite = (SKSpriteNode*)[self childNodeWithName:@"sprite"];

        // This works.
        sprite.position = location;

        SKAction *action = [SKAction moveByX:100 y:0 duration:1];

        // This doesn't.
        [sprite runAction:action];
    }
}

我一直在搜索互联网以及我自己的大脑,据我所知,我没有违反任何规则。请告诉我我错了,特别是我违反了哪条规则。因为如果事实证明这是一个不是我的错的问题,那么挫败感会大得多。我在做 iOS 7 中不可用的事情吗?

注意:我对通用示例 SpriteKit 项目(Spaceship 项目)所做的唯一更改是我在 SKScene 编辑器中添加了一个普通的红色精灵,并为其命名"sprite",修改touchesBegan:方法,如上图

另请注意: 以编程方式添加精灵(如在示例项目中)不会引发此问题,使用 childNodeWithName: 获取对它的引用就可以了美好的。这个问题似乎只在访问存档的 sks 文件中包含的精灵时出现。

这是崩溃时的痕迹:

* thread #1: tid = 0x4da6c, 0x00000000, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=1, address=0x0)
frame #0: 0x00000000
frame #1: 0x009b65d7 SpriteKit`SKCSprite::update(double) + 231
frame #2: 0x009b66de SpriteKit`SKCSprite::update(double) + 494
frame #3: 0x0099bd58 SpriteKit`-[SKNode _update:] + 43
frame #4: 0x0097c003 SpriteKit`-[SKScene _update:] + 104
frame #5: 0x009919a4 SpriteKit`-[SKView(Private) _update:] + 253
frame #6: 0x0098f5e1 SpriteKit`-[SKView renderCallback:] + 936
frame #7: 0x0098cea0 SpriteKit`__29-[SKView setUpRenderCallback]_block_invoke + 82
frame #8: 0x009b168f SpriteKit`-[SKDisplayLink _callbackForNextFrame:] + 268
frame #9: 0x009b19ad SpriteKit`-[SKDisplayLink _caDisplayLinkCallback] + 137
frame #10: 0x048cdd66 QuartzCore`CA::Display::DisplayLinkItem::dispatch() + 48
frame #11: 0x048cdc22 QuartzCore`CA::Display::DisplayLink::dispatch_items(unsigned long long, unsigned long long, unsigned long long) + 310
frame #12: 0x048ce147 QuartzCore`CA::Display::TimerDisplayLink::callback(__CFRunLoopTimer*, void*) + 123
frame #13: 0x006d4ac6 CoreFoundation`__CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__ + 22
frame #14: 0x006d44ad CoreFoundation`__CFRunLoopDoTimer + 1181
frame #15: 0x006bc538 CoreFoundation`__CFRunLoopRun + 1816
frame #16: 0x006bb9d3 CoreFoundation`CFRunLoopRunSpecific + 467
frame #17: 0x006bb7eb CoreFoundation`CFRunLoopRunInMode + 123
frame #18: 0x02b6a5ee GraphicsServices`GSEventRunModal + 192
frame #19: 0x02b6a42b GraphicsServices`GSEventRun + 104
frame #20: 0x00aa1f9b UIKit`UIApplicationMain + 1225
* frame #21: 0x00061a2d SKTest`main(argc=1, argv=0xbff9f39c) + 141 at main.m:14
frame #22: 0x027606d9 libdyld.dylib`start + 1

我 运行 进入了这个,这是我黑进到位的解决方法。是的,这并不理想,但肯定能避免崩溃。

加载场景后复制 SKSPriteNode 并像这样交换新节点:

 SKSpriteNode *sprite = (SKSpriteNode*)[self childNodeWithName:@"bob"];
 SKSpriteNode *newSprite = [sprite copy];
 [sprite removeFromParent];
 [self addChild:newSprite];

现在,您可以在该新节点上使用 SKAction,但是您喜欢使用 touchesBegan 方法中的相同代码。

我的猜测是基于 .sks 的节点的 属性 以某种方式无法正确初始化。

如果您有很多节点,您可能可以 loop/enumerate 通过它们来进行此交换。

更新

发帖人更进一步,发现创建 GameScene 的副本也能达到目的。因此无需循环遍历节点并单独制作副本。