gcd/dispatch_async и CoreData зависают в приложении

По какой-то причине совместное использование dispatch_async и Core Data приводит к тому, что мое приложение полностью зависает, но не падает.

Симптом

Пользовательский интерфейс не отвечает. Приложение не вылетает.

Приложение использует [[UIAccelermeter sharedAccelerometer] setDelegate: self] для непрерывного получения значений акселерометра. Обратный вызов "accelerometer:didAccelerate:" выполняется в основном потоке. При возникновении этой проблемы обратный вызов больше не выполняется.

Поэтому основной поток оказывается "замороженным".

Контекст

Я использую NSURLConnection в своем приложении для отправки HTTP-запросов на мой сервер. В connectionDidFinishLoading, где обрабатываются данные ответа, выполнение занимает около 2 секунд, и пользовательский интерфейс не отвечает в течение этих 2 секунд, поскольку он выполняется в основном потоке.

После некоторых исследований кажется, что gcd/dispatch_async является лучшим решением для этой ситуации, чтобы выполнить обработку данных в фоновом режиме. Итак, в connectionDidFinishLoading я добавил следующие вызовы gcd:

dispatch_queue_t queue;
queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(queue, ^{
    [self handleTheData: connection];
});

Иногда это работает нормально, но это может привести к случайному зависанию приложения. Несмотря на то, что используется один и тот же код с одними и теми же данными в хранилище Core Data, повторные тесты показывают, что симптом можно наблюдать в X% случаев. Вероятность возникновения проблемы зависит от нескольких факторов, описанных ниже.

Что делает handleTheData()?

Он проверяет данные ответа от NSURLConnection, которые представляют собой строку в формате JSON. Строка представляет собой 3 списка сущностей, и каждая сущность представляет собой массив массивов чисел. Итак, есть коды для преобразования строки в формате JSON в 3 NSMutableArray настраиваемого объекта, где каждый объект является подклассом NSManagedObject.

Во время преобразования, поскольку эти объекты являются «временными» в том смысле, что они могут храниться или не храниться постоянно, они создаются в «нулевом» NSManagedObjectContext, например:

SampleEntity *newEntity= 
(SampleEntity *) [[NSManagedObject alloc] initWithEntity:entity 
                       insertIntoManagedObjectContext:nil
               ];

Таким образом, они не будут смешиваться с основным контекстом.

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

[managedObjectContext save: &error] часто вызывается, чтобы обеспечить постоянное сохранение изменений.

Что-то еще работает в фоновом режиме?

Как упоминалось в разделе «Симптомы», акселерометр работает в фоновом режиме в основном потоке с использованием [[UIAccelermeter sharedAccelerometer] setDelegate: self]

AudioUnits также настроены для работы в фоновом режиме (в отдельном «приоритетном» потоке согласно документам Apple) для воспроизведения звука.

Выводы по причине проблемы

Было установлено, что на вероятность возникновения проблемы влияют следующие факторы:

  • Если какой-либо отладочный код закомментирован, например NSLog(@"fetchedResult: %@", fetchedResult), вероятность возникновения проблемы уменьшается (но она все еще возникает).
  • Если акселерометр не используется, проблема почти никогда не возникает (но не на 100%, так как я провел ограниченное количество тестов).
  • Если используется акселерометр, вероятность возникновения проблемы составляет примерно 50/50.
  • Если некоторые из кодов основных данных, такие как [sampleEntity setXYZ..], закомментированы, проблема почти никогда не возникает, даже если используется акселерометр.

Второй выпуск

Кроме того, даже если основной поток не заморожен, возникает ДРУГАЯ ПРОБЛЕМА. Код обработки данных завершится на полпути без ошибок.

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

Интересная проблема, не так ли? :)

Изменить

  • 01.03.2013 - Ранее я думал, что причиной проблемы была iOS 6. Извините, это было совершенно неправильно. Изменено описание, чтобы отразить новые результаты.

person Daniel    schedule 31.12.2012    source источник
comment
Одинаковы ли данные на iOS6 и iOS5? Если возможно, может быть, вы можете показать больше кода.   -  person CarmeloS    schedule 31.12.2012
comment
@Daniel Вы используете основные данные? Именно такая информация должна быть в вашем вопросе. Многопоточность основных данных требует определенного опыта. Если вы ожидаете какой-либо полезной помощи, вам действительно нужно опубликовать код.   -  person NJones    schedule 31.12.2012
comment
Спасибо, ребята, вы правы, я только что обнаружил, что это, скорее всего, вызвано Core Data. Попытка определить проблему сейчас и будет обновлена ​​​​как можно скорее. Был ослеплен тем фактом, что один и тот же код вызывает проблемы на одной модели, а не на другой.   -  person Daniel    schedule 31.12.2012
comment
Хорошо, ребята, извините за вводящее в заблуждение описание ранее, я потратил часы на эту проблему и получил новые выводы. См. обновленное описание выше.   -  person Daniel    schedule 03.01.2013


Ответы (1)


В итоге я обратился в Службу технической поддержки со службой поддержки разработчиков Apple. Я также предоставил им пример проекта, который показывает тот же симптом.

Через несколько дней я получил от них ответ, который указал мне на Руководство по программированию CoreData и указал на тот факт, что коды, запускаемые очередями отправки, должны иметь свой собственный частный NSManagedObjectContext.

https://developer.apple.com/documentation/coredata/using_core_data_in_the_background

После того, как я последовал совету, проблема, кажется, решена!

person Daniel    schedule 13.01.2013