【问题标题】:EXC_BAD_ACCESS (code=1, address=0x0) in SpriteKitSpriteKit 中的 EXC_BAD_ACCESS(代码=1,地址=0x0)
【发布时间】:2014-10-24 21:52:47
【问题描述】:

我目前正在为 iPhone 编写游戏,有时我会随机崩溃并显示标题中的错误。

我已经研究了很多,它可能与泄漏(?)内存问题有关。我问过的人告诉我,这可能与添加一个 nil 对象有关。我设置了异常断点和 NSZombies,但它们都没有识别崩溃,也没有给我发生崩溃的确切代码行。

经过一些崩溃测试后,我注意到大多数情况下,当 touchesBegan 方法或 didBeganContact 方法处于活动状态时会发生这种情况。

touchesBegan 方法和 didBeginContact 方法的代码如下:

-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    if (touchEnabled == YES){
    firstTouch++;
    if (firstTouch == 1){
        SKAction* removeFade = [SKAction removeFromParent];
        SKAction* startFade = [SKAction fadeAlphaTo:0 duration:0.5];
        SKAction* fadeSeq = [SKAction sequence:@[startFade, removeFade]];
        [startScreen runAction:fadeSeq completion:^{
            startScreenRemoved = YES;
            [self gameCountdown];
            touchToShoot = YES;
        }];   
    }
    if (firstTouch == 2){
        weaponActivated = YES;
    }

    if (gameOver == YES){
        [self removeAllActions];
        [self removeAllChildren];
        [bgPlayer stop];
        touchToShoot = NO;
        SKScene* gameScene = [[GameScene alloc] initWithSize:self.size];

        SKTransition *doors = [SKTransition doorsOpenVerticalWithDuration:1];

        [self.view presentScene:gameScene transition:doors];
        gameOver = NO;
               }

    }

    if (touchToShoot == YES) {

        [self weaponParticle];

        touchToShoot = NO;

        [self performSelector:@selector(enableTouchToShoot) withObject:nil afterDelay:0.3]; //-enableTouchToShoot{} = touchToShoot = YES;

    }

    //Pause Button
    UITouch *touch = [touches anyObject];
    CGPoint location = [touch locationInNode:self];
    SKNode *node = [self nodeAtPoint:location];

    if ([node.name isEqualToString:@"pauseButton"]) {
        [self pauseMenu];

    }
    else if ([node.name isEqualToString:@"continue"]) {

        self.paused = NO;

        pauseBG.hidden = YES;
        pauseText.hidden = YES;
        backm.hidden = YES;
        cont.hidden = YES;

        //Damit unsichtbare Buttons nicht während dem Spiel gedrückt werden

        backm.zPosition = -100;

        cont.zPosition = -100;

    }
    else if ([node.name isEqualToString:@"backtomenu"]) {
        self.paused = NO;
        [self displayAlert];
    }

}

-(void)didBeginContact:(SKPhysicsContact *)contact{   

    if (contact.bodyA.categoryBitMask == shipCategory) {

        //Explosion Animation
        SKAction* wait =[SKAction waitForDuration:0.5];
        SKAction* fadeOut = [SKAction scaleTo:0.0 duration:1];
        remove = [SKAction removeFromParent];


        explodeSequenceGO =[SKAction sequence:@[fadeOut,wait,remove]];


        ExplosionPath = [[NSBundle mainBundle] pathForResource:@"Explosion" ofType:@"sks"];
        Explosion = [NSKeyedUnarchiver unarchiveObjectWithFile:ExplosionPath];
        Explosion.position = CGPointMake(contact.bodyA.node.position.x, contact.bodyA.node.position.y);
        [self addChild:Explosion];
        [Explosion runAction:explodeSequenceGO];

        [contact.bodyA.node removeFromParent];
        [contact.bodyB.node removeFromParent];
        [shipSmoke removeFromParent];
        [player1 removeFromParent];

        touchEnabled = NO;
        [self gameOver];

    }


    if (contact.bodyA.categoryBitMask == enemyCategory || contact.bodyB.categoryBitMask == enemyCategory) {

        //Explosion Animation
        SKAction* wait =[SKAction waitForDuration:0.5];
        SKAction* fadeOut = [SKAction scaleTo:0.0 duration:1];
        remove = [SKAction removeFromParent];
        explodeSequenceGO =[SKAction sequence:@[fadeOut,wait,remove]];

        ExplosionPath = [[NSBundle mainBundle] pathForResource:@"Explosion" ofType:@"sks"];
        Explosion = [NSKeyedUnarchiver unarchiveObjectWithFile:ExplosionPath];
        Explosion.position = CGPointMake(contact.bodyA.node.position.x, contact.bodyA.node.position.y);

        [Explosion runAction:explodeSequenceGO];
        [self addChild:Explosion];
        [contact.bodyA.node removeFromParent];
        [contact.bodyB.node removeFromParent];


        hitCount++;
        [self scoreChange:100];
    }

    if (hitCount>39) {
        [self eLevel2];
    }

}

有人看到过错吗?我非常感谢任何提示,因为我正在寻找这个错误数周......

编辑:崩溃只是指向“main”函数,这根本没有帮助

在每个线程“动作”中,它只指向 Assembly(?) 代码:

就像我说的,我尝试通过各种调试工具(NSZombies、MemoryTool、Exceptional Breakouts 等)分析崩溃,但它们都没有给我有用的信息。当应用程序崩溃时,调试工具会停止记录,但不会显示任何故障或崩溃结果。

【问题讨论】:

  • 哪一行给出了错误?像这样丢掉一堆代码然后让我们筛选它不是很有效。
  • 您可以从 Xcode 中复制并粘贴调用堆栈以及几乎所有其他内容作为文本,只需选择它并按 Cmd+C。图像使事情难以阅读。一个提示:如果您呈现一个新场景,请确保没有其他代码以相同的方法运行。您应该在那里添加一个返回,否则您可能会在 0.3 秒后在当前场景上执行选择器,而它可能已经被释放。

标签: objective-c crash sprite-kit exc-bad-access


【解决方案1】:

我知道这已经有几个月了,但在从一个场景转换到另一个场景时,我也遇到了 EXC_BAD_ACCESS 错误。在阅读了多篇文章并注释掉了我游戏中的几乎每一行代码之后,为我解决的问题是在从方法返回之前从现有场景中删除所有对象并且爆炸错误消失了。

- (void)returnToMenuScene {

    SKTransition *reveal = [SKTransition revealWithDirection:SKTransitionDirectionDown duration:1.0];
    MenuScene *menuScene = [MenuScene sceneWithSize:self.scene.size];
    [self.scene.view presentScene:menuScene transition:reveal];

    [self.gameScene removeAllChildren];

    return;
}

- (GameScene *)gameScene {

    return (GameScene *)self.scene;
}

我希望这对将来的其他人有所帮助

【讨论】:

    【解决方案2】:

    我早期发现的一件事是,如果原始场景被 SpriteKit 过早杀死,场景之间的转换会导致崩溃。 - 我知道的疯话。

    为了解决这个问题,我创建了一个包含清理操作的基类场景。

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

    然后当我转换场景时,我让旧场景存活一段时间,然后再销毁它。 注意在旧场景上使用一个动作来稍后清理自己。 称我为偏执狂,妄想症,但它解决了我的崩溃。当我在 App 上收到 Terminate 事件时,我还需要调用 cleanup。

    if( oldscene!=nil )
    {
        SKTransition* doors = [SKTransition doorsOpenVerticalWithDuration:1.5];
        doors.pausesIncomingScene = NO;
        [[self view] presentScene:newscene transition:doors];
    
        SKNode* dummynode = [SKNode node];
    
        // wait for the transition to complete before we give up the referene to oldscene
        [dummynode runAction:[SKAction waitForDuration:2.0] completion:^{
            if ([oldscene isKindOfClass:[MyBaseScene class]])
            {
                // failing to clean up nodes can result in crashes... nice.
                [((MyBaseScene*)oldscene) cleanUp];
            }}];
        [oldscene addChild:dummynode];
    }
    else
    {
        [[self view] presentScene:newscene];
    }
    

    【讨论】:

    • 似乎更有可能在其他地方引用了您的场景,应用程序在删除后尝试访问它。您在这种情况下的延迟将确保它仍然可以访问,因此不会出错。人们对场景有某种参考但没有意识到这一点似乎很常见。什么使用该参考以及如何使用,确定您是否会收到错误。修复可能只是在转换之前更新对新场景的引用。
    • 我通常会同意你的观点,但在我做任何复杂的事情之前我就经历过这种情况——并且正在使用 ARC。不过,我必须马上把它煮回去才能确定!
    • 我发现了我这样做的原因 - 因为错误的 SKShapeNode 问题 - stackoverflow.com/questions/22399278/…
    • 我发现在 iOS 8+ 中我不再需要这样做,因此停止支持 iOS 7 或更低版本并删除了我的代码!
    猜你喜欢
    • 2016-09-21
    • 1970-01-01
    • 1970-01-01
    • 2015-03-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多