1

I am currently coding a game for the iPhone and I sometimes get random crashes with the error in the title.

I've researched quite a bit and it probably has to do with leaking(?) memory issues. The people I asked told me, that it may has to do with adding a nil object. I set up exception breakpoints and NSZombies but neither of those recognize the crash, nor give me the exact code line where the crash occurs.

After some crash-testing I noticed that most of the time it happens when either the touchesBegan method or the didBeganContact method is active.

Here's the code of the touchesBegan method and the didBeginContact method:

-(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];
    }

}

Does anyone see a fault? I greatly appreciate any tip, since I am searching for this bug for weeks....

EDIT: The crash just points to the "main" function, which doesn't help at all

The crash just points to the "main" function, which doesn't help at all

And in each of the Thread "actions" it just points to Assembly(?) code: link

And like I said, I tried to analyze the crashes by various debug tools (NSZombies, MemoryTool, Exceptional Breakouts, etc.) but none of them give me useful infos. When the app crashes, the debugging tools just stop recording, but they don't show me any faults or crash results.

CodeSmile
  • 64,284
  • 20
  • 132
  • 217
user3545063
  • 681
  • 8
  • 17
  • What line gives the error? Just dropping a bunch of code like this and asking us to sift through it isn't very effective. – Almo Oct 24 '14 at 21:54
  • You can copy & paste the call stack and just about everything else from Xcode as text, just select it and press Cmd+C. Images make things hard to read. One tip: if you present a new scene, make sure no other code runs in that same method. You should add a return there, otherwise you may be performing a selector on the current scene after 0.3 seconds when it may have already been deallocated. – CodeSmile Oct 25 '14 at 09:42

2 Answers2

2

I know that this is a few months old, but I too was experiencing the EXC_BAD_ACCESS error when transitioning from one scene to another. After reading multiple posts, and commenting out almost every line of code in my game, what fixed it for me was the removing all of the objects from the existing scene before returning from the method and bang error was gone.

- (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;
}

I hope this helps someone else in the future

Ron Myschuk
  • 6,011
  • 2
  • 20
  • 32
1

One thing I found early on is that transitioning between scenes caused crashes if the original scene was killed too early by SpriteKit. - Crazy talk I know.

To help this I created a base class scene which included a cleanup op.

-(void)cleanUp
{
    [self cleanUpChildrenAndRemove:self];
}


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

Then when i transition scenes, I let the old scene live for a while before destroying it. Notice the use of an action on the old scene to clean itself up later on. Call me paranoid, delusional, but it fixed my crash. I also needed to call cleanup when I received Terminate events on the App.

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];
}
GilesDMiddleton
  • 2,279
  • 22
  • 31
  • It's seems more likely that there is a reference to your scene elsewhere that the app tries to access AFTER it's been removed. Your delay in that case would ensure it's still accessible and therefore no error. It seems common that people have a reference of some sort to the scene and don't realize it. What uses that reference and how, determines whether you'd get the error. The fix might be simply updating that reference to the new scene before the transition. – prototypical Oct 25 '14 at 18:03
  • I would normally agree with you but I experienced this before I did anything complex- and am using ARC. I'd have to boil it right back now to be sure though! – GilesDMiddleton Oct 27 '14 at 18:17
  • I found out the reason I was doing this - because of the buggy SKShapeNode issue - http://stackoverflow.com/questions/22399278/sprite-kit-ios-7-1-crash-on-removefromparent – GilesDMiddleton Nov 10 '14 at 18:21
  • I have found that in iOS 8+ I no longer need to do this, so have stopped supporting iOS 7 or below and removed my code! – GilesDMiddleton Jun 01 '15 at 15:19