0

I have a game using sprite kit and objective-C originally developed using Xcode 6 and iOS 7, but it now crashes on iOS 9 devices.

The crash occurs when the user is progressing from one level to another and a new horizontal scrolling background (SKNode) is added to the scene.

Exception:

Thread 1 EXC_BAD_ACCESS (code=1, address=0x0)

Adding the horizontal scrolling background to the scene when changing level:

// Array of planets to scroll
NSArray *parallaxBackgroundNames = @[@"bg_galaxy.png", @"bg_planetsunrise.png",
                                     @"bg_spacialanomaly.png", @"bg_spacialanomaly2.png"];
CGSize planetSizes = CGSizeMake(200.0, 200.0);


// Initialize new back round scrolling node and ad to scene 
_parallaxNodeBackgrounds = [[FMMParallaxNode alloc] initWithBackgrounds:parallaxBackgroundNames
                                                                   size:planetSizes
                                                   pointsPerSecondSpeed:10.0];

// Position in center of screen
_parallaxNodeBackgrounds.position = CGPointMake(self.size.width/2.0, self.size.height/2.0);

// Method to randomly place planets as offsets to center of screen
[_parallaxNodeBackgrounds randomizeNodesPositions];

// EXCEPTION THROWN HERE when adding it to the layer
[self addChild:_parallaxNodeBackgrounds];

The _parallaxNodeBackgounds is an SKSNode instance of FMMParallaxNode initialized as:

- (instancetype)initWithBackgrounds:(NSArray *)files size:(CGSize)size pointsPerSecondSpeed:(float)pointsPerSecondSpeed
{
    if (self = [super init])
    {
        _pointsPerSecondSpeed = pointsPerSecondSpeed;
        _numberOfImagesForBackground = [files count];
        _backgrounds = [NSMutableArray arrayWithCapacity:_numberOfImagesForBackground];
        _randomizeDuringRollover = NO;
        [files enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
            SKSpriteNode *node = [SKSpriteNode spriteNodeWithImageNamed:obj];
            node.size = size;
            node.anchorPoint = CGPointZero;
            node.position = CGPointMake(size.width * idx, 0.0);
            node.name = @"background";
            //NSLog(@"node.position = x=%f,y=%f",node.position.x,node.position.y);
            [_backgrounds addObject:node];
            [self addChild:node];
        }];
    }
    return self;
}

Any input as to why this crash happens is appreciated.

EDIT: My understanding of this exception is that it is thrown if a pointer is pointing to memory which has been deallocated or the pointer is corrupt.

There are no errors associated with this object when I build and analyse in Xcode and I have also created a new pointer locally (the original pointer is a instance var) but still get the same error.

dancingbush
  • 2,131
  • 5
  • 30
  • 66
  • you have a circular reference going here, so your FMMParallaxNode never deallocs, you need to add [unowned self] to the beginning of your block – Knight0fDragon Feb 10 '16 at 01:35
  • Thanks, I will try later. I did try to release the object but compiler thrown error stating this was not permitted. Any idea why this is an issue in ios9 only? – dancingbush Feb 10 '16 at 13:07
  • Tighter restrictions on 9 perhaps – Knight0fDragon Feb 10 '16 at 13:43
  • Actually the initialisation of FMMParallaxNode doesn't occur inside a block so can't call [unowned self]- Initialisation occurs on the main thread in a switch statement, also it is already assigned a value at the start of the game for level 1 – dancingbush Feb 10 '16 at 21:45
  • What? oh this is objective C, I was thinking swift, in object c, you need to create a weak reference to self outside of the block, `__weak id weakSelf = self;` and use `weakSelf` in the block – Knight0fDragon Feb 10 '16 at 21:49
  • Ok but the code is not actually executed inside a block – dancingbush Feb 10 '16 at 22:03
  • What does that matter? your object is still in memory and can never delete – Knight0fDragon Feb 10 '16 at 22:13
  • You are creating a block inside of your init, this block creates a strong reference to your object, your object will not release from memory until the block clears, but the block will not release from memory till the object clears, this is called circular referencing. Eventually you run out of memory, your inits will fail, and you will get a bad access – Knight0fDragon Feb 10 '16 at 22:18
  • not sure where exactly to create this weak ref, get errors if I declare in FMMParallaxNode.m class (Expected ';" at end of declaration list) or the SKScene.m class (where the Bad Exec exception is thrown) – dancingbush Feb 10 '16 at 22:59

1 Answers1

1

Do a weak reference like this:

- (instancetype)initWithBackgrounds:(NSArray *)files size:(CGSize)size pointsPerSecondSpeed:(float)pointsPerSecondSpeed
{
    if (self = [super init])
    { 
        __weak FMMParallaxNode *weakSelf = self;
        _pointsPerSecondSpeed = pointsPerSecondSpeed;
        _numberOfImagesForBackground = [files count];
        _backgrounds = [NSMutableArray arrayWithCapacity:_numberOfImagesForBackground];
        _randomizeDuringRollover = NO;
        [files enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
            SKSpriteNode *node = [SKSpriteNode spriteNodeWithImageNamed:obj];
            node.size = size;
            node.anchorPoint = CGPointZero;
            node.position = CGPointMake(size.width * idx, 0.0);
            node.name = @"background";
            //NSLog(@"node.position = x=%f,y=%f",node.position.x,node.position.y);
            [_backgrounds addObject:node];
            [weakSelf addChild:node];
        }];
    }
    return self;
}
Knight0fDragon
  • 16,609
  • 2
  • 23
  • 44