Search code examples
iosobjective-carrayscocos2d-iphone

Bad Access using NSMutableArray with ARC


At the moment I am creating a game with cocos2d v3.1 using box2d, so I have to use .mm extensions for my source files to indicate that I am using c++. The game contains an „endless terrain“ that consists of terrain-key-points. I created a class that saves those key points named TerrainElement. At the beginning of the game I create an NSMutableArray called _elements array and fill it with random TerrainElements.

Within the game loop I check I iterate over the elements, and draw all those elements that are now visible. However the App crashes after a rather random amount of time.

Header

@interface TerrainBase : CCNode{
     NSMutableArray * _elements;
}

part of the game loop:

while (((TerrainElement*)[_elements objectAtIndex:toKeyPoint]).startPoint.x < _currentX+winSize.width*9/8) {

    //Crashes sometimes here EXC_BAD_ACCESS, _elements array is NULL
     TerrainElement *element = (TerrainElement*)[_elements objectAtIndex:toKeyPoint];


    [element drawElement];
    toKeyPoint++;

}

I was not able to figure out why the NSMutableArray was NULL. I just iterate over the elements in each loop, and do not touch them basically. I made a property out of the _elements Array(nonatomic,strong), which did not help either. I decided to use a normal array instead of the NSMutableArray. I did not change the code at all, except for the indexing of the array:

@interface TerrainBase : CCNode{
    TerrainElement *_elements[4000];
}

gameLoop:

while ( _elements[toKeyPoint].startPoint.x< _currentX+winSize.width*9/8) {

            [_elements[toKeyPoint] drawElement];

    toKeyPoint++;

}

This time everything works, and the array is never NULL. Its a mystery to me why the second solution works but the first keeps crashing. I would really prefer to use the NSMutableArray as it gives me more flexibility. Can anybody help?


Solution

  • you are allowed to send messages to nil (NULL) so that is not a reason to crash on its own (you would just get nil back in this case).

    I'm not sure you have given enough information to determine an actual cause. Please post the stacktrace and any exceptions. does your posted code contain everything happening in that loop?

    your loop condition looks like flawed logic to me in a couple of ways:

    1. unless your array is sorted by startPoint.x you will stop drawing terrain as soon as you encounter the first element >= _currentX+winSize.width*9/8
    2. as I stated above, you can send messages to nil and you will get nil (or NULL or 0 or whatever you want to call it) back for the value returned. This means that you have an infinite loop whenever _elements is nil (because [_elements objectAtIndex:toKeyPoint]).startPoint.x will always evaluate to 0 which is always < _currentX+winSize.width*9/8 presumably.

    I need more information to help you further.