iPhone EXC_BAD_ACCESS с NSMutableArray из строк - PullRequest
1 голос
/ 23 сентября 2011

Проблемным контекстом является ViewController с несколькими обработчиками кнопок и файлом данных Scores.list из 1000 объектов NSString. Если я нажимаю на buttonOne, код обработчика проверяет, существует ли файл Scores.list в каталоге пользовательских документов. Если да, он загружает данные в NSMutableArray с именем Score, если нет, он создает NSMutableArray в памяти (для последующего хранения на диске) следующим образом:

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

В основном этот код работает; в обоих случаях (чтение данных из Scores.list или сборка in-mem) я могу проверить в отладчике (с помощью 'po Score'), что массив с 1000 записями присутствует впоследствии.

Теперь приходит проблема, которая блокирует меня на 2 дня:

В обработчике buttonTwo такие операторы, как [count count], аварийно завершают работу, но только в том случае, если оценка массива получает данные с диска, а не при накоплении в памяти. В первом случае это массив остается в силе, хотя до последней строки кода обработчика buttonOne, но затем «испаряется», как только массив обращается к следующему обработчику (EXC_BAD_ACCESS).

Нет, это не вызвано преждевременным заявлением о выпуске, так как его нет (пока) во всем моем приложении. :) (пока не касается утечек памяти).

Как это возможно, что в одном представлении NSMutableArray действителен в конце обработчика кнопки 1, но недействителен в начале следующего обработчика кнопки 2, если между ними не выполняется явный оператор освобождения?

Дополнительная информация: ViewController.h

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

А в ViewController.m у меня есть:

@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. Я попробовал NSZombieEnabled, запустив приложение с помощью alt / cmd / R и добавив «NSZombieEnabled = YES», но это не приводит к дополнительной информации в консоли отладки.

1 Ответ

2 голосов
/ 28 сентября 2011

Это всегда немного опасно:

score = [[NSMutableArray alloc] init];

вне метода init. Потому что проблема в том, что, возможно, ваш метод readScores выполняется дважды. Я думаю, это почти наверняка так.

Что происходит, то первый scores объект никогда не освобождается. Это вызывает утечку памяти, и рано или поздно вы получите страшные EXC_BAD_ACCESS.

Итак, лучше всего сначала проверить:

if (score)
{
    [score release];
}

Или, альтернативно, настройте scores как синтезированный сохраненный объект в вашем файле .h. Затем вы можете заменить свой код следующим образом и позволить всему происходить автоматически:

self.scores = [NSMutableArray array];

Обратите внимание, что здесь я не использовал:

self.scores = [NSMutableArray alloc] init];

потому что это может вызвать два удержания, а затем EXC_BAD_ACCESS из-за чрезмерного удержания.

...