objective-csprite-kitexc-bad-accessautorelease

EXC_BAD_ACCESS on 8 line spritekit code


My incredibly short app code somehow generates an EXC_BAD_ACCESS error. I have reproduced the error on a completely fresh app. But I can not comprehend how this happens.

Here is the entire code for my app (aside from what xcode generates automatically for a spritekit game).

-(void)didMoveToView:(SKView *)view {
    SKNode* pFirst = [SKNode node];
    [self addChild:pFirst];
    //
    SKSpriteNode* pSprite = [SKSpriteNode spriteNodeWithImageNamed:@"img61.png"];
    pSprite.centerRect = CGRectMake(0.4,0.4, 0.2, 0.2); // suspect 1
    pSprite.position = CGPointMake(0,0);
    pSprite.xScale = 2.0;
    pFirst.alpha = 0.0;             //  suspect 2
    //
    [pFirst addChild:pSprite];    
}

The debugger places the crash point here:

int main(int argc, char * argv[]) {
    @autoreleasepool {
        return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
    }
}

How can I understand what is wrong here?

Of course, this problem originally comes from a much longer app that was crashing. This is what code I had left when I simplified it, but preserved the crash.

Two lines are marked as "suspects". Amazingly, the code runs fine if EITHER suspect1 or suspect2 are removed. I can not comprehend this at all.


Solution

  • We may find some hint from the description of centerRect,

    A property that defines how the texture is applied to the sprite.

    So if you set centerRect of a sprite along with setting its alpha = 0.0, the alpha blended into the framebuffer will be 0 (See Creating a Textured Sprite for more information). From the current case we just tested, the zero-alpha sprite is treated as a null sprite. So adding a child node to the null sprite will cause crash.

    To solve the problem, one method is to change pFirst.alpha = 0.0; to pFirst.hidden = YES;. An alternative is to set the alpha after a sprite rendered to the scene. In your didMoveToView method, assign an identifier for pFirst,

    pFirst.name = @"firstNode"
    

    Then, add the following method to set alpha to zero after physics simulations are performed,

    - (void)didSimulatePhysics
    {
        [self enumerateChildNodesWithName:@"firstNode" usingBlock:^(SKNode *node, BOOL *stop) {
            if (node.alpha > 0.0) {
                node.alpha = 0.0;
            }
        }];
    }