iOS5 вылетает во время runMode: beforeDate: - PullRequest
10 голосов
/ 06 октября 2011

У меня проблема с совместимостью моего приложения с версиями iOS5 b7 и GM.

Проблема возникает в следующих строках кода:

do {
    [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
} while (!done);

Приложение вылетает с сигналом EXC_BAD_ACCESS после нескольких итераций.

Количество пройденных итераций является случайным (от 2 до 7).

Также все работает довольно хорошо на iOS4 и iOS3.

Та же проблема возникает в примере Apple: Пример XMLPerformance .

Что вы думаете об этом?

12 октября тысячи пользователей моего приложения обновятся до iOS5, и я не хочу, чтобы мое приложение было с такой странной ошибкой в ​​AppStore.

Ответы [ 4 ]

10 голосов
/ 06 октября 2011

4 часа прошло, и я нашел проблему. Я опишу, как я решил проблему в XMLPerformance sample.

Проблема была в NSAutoreleasePool. Есть @property (nonatomic, assign) NSAutoreleasePool *downloadAndParsePool;. Когда приложение начинает загружать Top300 Paid Apps RSS, создается новая тема с использованием [NSThread detachNewThreadSelector:@selector(downloadAndParse:) toTarget:self withObject:url];. Таким образом, в этом потоке мы должны хранить локальный пул автоматического выпуска. Это делается следующим образом:

- (void)downloadAndParse:(NSURL *)url {
    self.downloadAndParsePool = [[NSAutoreleasePool alloc] init];

    // initializing internet connection and libxml parser.
    if (rssConnection != nil) {
         do {
             [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
         } while (!done);
    }
    // Release resources used only in this thread.
    [downloadAndParsePool release]; 
    self.downloadAndParsePool = nil;
}

Так что в downloadAndParse: все выглядит хорошо. Теперь давайте рассмотрим один метод, который вызывается при разборе элемента из RSS:

- (void)finishedCurrentSong {
    // sending new item to delegate and other ...
    countOfParsedSongs++;
    // Periodically purge the autorelease pool. The frequency of this action may need to be tuned according to the 
    // size of the objects being parsed. The goal is to keep the autorelease pool from growing too large, but 
    // taking this action too frequently would be wasteful and reduce performance.
    if (countOfParsedSongs == kAutoreleasePoolPurgeFrequency) {
        [downloadAndParsePool release];
        self.downloadAndParsePool = [[NSAutoreleasePool alloc] init];
        countOfParsedSongs = 0;
    }
}

Как вы видите там строки:

[downloadAndParsePool release];
self.downloadAndParsePool = [[NSAutoreleasePool alloc] init];

Так что именно эти строки вызывают исключение. Если я их комментирую, все отлично работает.

Но я решил не только прокомментировать эти строки, но и заменить NSAutoreleasePool в - (void)downloadAndParse:(NSURL *)url блоком @autorelease, так как сказано, что он более эффективен:

- (void)downloadAndParse:(NSURL *)url {
@autoreleasepool {

    // initializing internet connection and libxml parser.
    if (rssConnection != nil) {
         do {
             [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
         } while (!done);
    }
    // Release resources used only in this thread.
    }
}

Теперь все отлично работает. Единственная проблема, которую я не решил:

// Periodically purge the autorelease pool. The frequency of this action may need to be tuned according to the 
// size of the objects being parsed. The goal is to keep the autorelease pool from growing too large, but 
// taking this action too frequently would be wasteful and reduce performance.

Так что, если у кого-то есть какие-либо мысли по поводу этой проблемы, можно опубликовать еще один ответ и, возможно, попытаться объяснить более правильно исправление ошибки. Я буду рад принять этот ответ.

Спасибо.

2 голосов
/ 06 октября 2011

Это похоже на проблема с памятью , пожалуйста, проверьте Apple Technote QA1367 " Поиск ошибок EXC_BAD_ACCESS в проекте Какао "

В вашем коде попробуйте это на сбой как можно скорее :

[item release], item = nil;

Это не решает проблему, просто делает сбой раньше и, мы надеемся, даст вам более содержательный стэк для изучения.

Если вы используете многопоточность , хорошо ... Вы можете попытаться вывести «текущий» идентификатор потока в консоль, чтобы убедиться, что все действительно выполняется в потоке, где вы ожидаете, что они будут работать.Особенно убедитесь, что все элементы пользовательского интерфейса находятся в основном потоке, даже если такой код запускается как побочный эффект другого кода (возможно, всплывающих окон с ошибками).

#include <pthread.h>
- (void)myFunction
{
    NSLog(@"Thread (%d)",
        pthread_mach_thread_np(pthread_self()));
}

Запустите приложение с помощью Instruments , убедитесь, что проверка памяти происходит каждые 1 или 2 секунды.Медленно, но еще раз вы хотите получать уведомления как можно ближе к реальной проблеме памяти.

1 голос
/ 06 октября 2011

Глядя на ваш код: откуда взялась эта "done" переменная и кто меняет ее значение и когда? Теперь это выглядит довольно волшебно.

Также вы можете проверить возвращаемое значение runMode: beforeDate , чтобы убедиться, что оно было запущено. Если возвращаемое значение НЕТ, runLoop вообще не запускался. Может быть, какая-то другая часть вашего кода не может обработать такой случай?

0 голосов
/ 12 октября 2011

Просто мой маленький вклад.

Поскольку у меня та же проблема, я обнаружил, что в iOS5 вам не нужно иметь свой собственный NSAutoreleasePool в потоке (используется executeSelectorOnMainThread).

Затем, в вашем коде (xml-парсер, такой же как и я), я думаю, что вы должны отделить код от iOS4 и iOS5.

В iOS4 вам нужен NSAutoreleasePool, но не в iOS5.

...