开发者

iPhone EXC_BAD_ACCESS with NSMutableArray of strings

Problem context is a ViewController with several button handlers and a scores.list data file of 1000 NSString objects. If I click on buttonOne, the handler code checks if the file scores.list exists in the User Documents directory. If yes, it loads the data in an NSMutableArray called score, if not it creates the NSMutableArray in memory (to be stored on disk later) like this:

- (void)readScores
{
    // Setup path + filename pathUserDocDirScorelist);
    ...
    // test for presence scores.list in documents dir
    if ([fileManager fileExistsAtPath: pathUserDocDirScorelist])
    {   // Read scores.plist into NSMutableArray
        score = [NSMutableArray arrayWithContentsOfFile:pathUserDocDirScorelist];
    } else { // Initialize empty scores array with 1000 empty NSString entries
        score = [[NSMutableArray alloc] init];
        int i;
        for(i = 0; i < 1000; i++) {
            [score addObject:@""];
        }
    }
    // at this point there is always a valid array score with 1000 entries
}

Basically this code works; in both cases (reading data from scores.list or in-mem build开发者_如何转开发-up) I can verify in the debugger (with 'po score') that an array with 1000 entries is present afterwards.

Now comes the problem that is blocking me for 2 days now:

In the handler of buttonTwo, statements like [score count] crash, but only in case the array score gets its data from disk, not if build-up in memory. In the first case is the array still valid though until the last line of handler code of buttonOne, but then 'evaporates' as soon as the array is addressed in a next handler (EXC_BAD_ACCESS).

No, it is not caused by a premature release statement, since there are none (yet) in my entire app. :) (not concerned with memory leaks yet).

How is this possible that within one view a NSMutableArray is valid at the end of button handler 1, but invalid at the beginning of the next button handler 2 if no explicit release statement is executed in between?

Extra info: ViewController.h:

@interface ViewController : UIViewController {
    NSMutableArray *score;
    ...
}
@property (nonatomic, retain) NSMutableArray *score;
...
- (IBAction) buttonOne: (id) sender;
- (IBAction) buttonTwo: (id) sender;
@end

And in ViewController.m I have:

@synthesize score;
...

- (IBAction) buttonOne: (id) sender {
    if (score == nil) {
        [self readScores];
    }
    ...
    NSLog(@"Number of entries in score = %i", [score count]); // never crashes
}


- (IBAction) buttonTwo: (id) sender {
    NSLog(@"Number of entries in score = %i", [score count]); // **crash point**
}

P.S. I tried NSZombieEnabled by starting the app with alt/cmd/R and adding 'NSZombieEnabled=YES', but that does not result in extra information in the debug console.


It's always a little dangerous to do this:

score = [[NSMutableArray alloc] init];

outside of an init method. Because, the problem is that perhaps your method readScores is executed twice. I'd guess it almost certainly is.

What happens then is the the first scores object is never released. That causes a memory leak and sooner or later you get the dreaded EXC_BAD_ACCESS.

So, the best thing is either to first check:

if (score)
{
    [score release];
}

Or, alternatively, set up scores as a synthesized retained object in your .h file. Then you can replace your code as follows and let everything happen automatically:

self.scores = [NSMutableArray array];

Note that here, I didn't use:

self.scores = [NSMutableArray alloc] init];

because that would cause two retains to happen, and of then EXC_BAD_ACCESS due to over-retention.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜