У меня есть многопоточное приложение, которое использует Core Data. Я видел много сбоев при запуске и различные причудливые сообщения об ошибках. Однако иногда это работает отлично! Я никогда не видел аварии на моем собственном iPhone4, но он рушится на других устройствах. Думаю, я выяснил эту проблему, но не знаю, как ее лучше всего решить.Основные данные - многопоточное состояние гонки при запуске
Когда приложение запускается, я использую GCD для загрузки данных из Интернета и обновления данных ядра в фоновом потоке. В основном потоке я продолжаю настройку табличных представлений, и одна из этих команд запускает приемник NSManagedObjectContext
. Я использую в значительной степени стандартный код шаблона, так как это первый раз, когда это выполняется, обычный ленивый код инициализации создает NSPersistentStoreCoordinator
.
Однако фоновый поток создает новый NSManagedObjectContext
для его использования. Это включает в себя получение NSPersistentStoreCoordinator
следующим образом:
// Create a new NSManagedObjectContext for this thread
NSManagedObjectContext *threadContext = nil;
NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
if (coordinator != nil)
{
threadContext = [[NSManagedObjectContext alloc] init];
[threadContext setPersistentStoreCoordinator:coordinator];
NSMergePolicy *mergePolicy = [[NSMergePolicy alloc] initWithMergeType:NSMergeByPropertyObjectTrumpMergePolicyType];
[threadContext setMergePolicy:mergePolicy];
} else {
NSLog(@"Error - No NSPersistentStoreCoordinator");
}
Я 90% уверен, что проблема в том, что оба потока получают в NSPersistentStoreCoordinator
код одновременно. Обе нити считают объект nil
, поэтому создайте объект. Это вызывает проблемы, связанные с тем, что первый поток попадает туда, когда ivar внезапно указывает на неправильное место. В этот момент происходят плохие вещи!
Итак, как лучше всего это решить? Я временно исправил проблему, добавив sleep(1)
в фоновый поток :), но я не уверен, что это действительно лучшее решение! Я попытался изменить свойства NSPersistentStoreCoordinator
так, чтобы они были атомарными, но это не помогло. Должен ли я обернуть каждый из стандартных геттеров в @synchronise
для блокировки мьютексов? У меня еще не было времени попробовать, но он должен остановить тот же код, который запускается обоими потоками. Или есть лучший способ?
Мысли?
Благодаря Craig
======================================
Для справки, вот некоторые из ошибок и сбоев, которые я видел. Они довольно случайны, но это может помочь другим найти этот пост в будущем.
2012-01-18 22: 19: 57,586 БФК [10468: 11d03] - [NSSQLModel _addPersistentStore: идентификатор]: непризнанная селектор отправляется например 0x6b80390
2012-01-18 22:19 : 57,595 БФК [10468: 11d03] * Согласующий приложение из-за неперехваченного исключением 'NSInvalidArgumentException', причина: '- [NSSQLModel _addPersistentStore: идентификатор]: непризнанная селектор послан к экземпляру 0x6b80390'
2012-01- 19 16: 58: 06.671 CBF [11738: fe03] - [__ NSCFDictionary_hasPrecomputedKeyOrder]: непризнанные селектор направлен например 0x6d55040
2012-01-25 21: 45: 31.174 CBF [16911: 1310b] - [__ NSArrayM _addPersistentStore: идентификатор]: непризнанная селектор отправляется например 0x6d59370
2012-01-25 21:45:31.175 CBF [16911: 1310b] * Нагрузочный приложение из-за неперехваченное исключение 'NSInvalidArgumentException', причина: '- [__ NSArrayM _addPersistentStore: идентификатор]: непризнанные селектор направил к экземпляру 0x6d59370'
Поблагодарить Пол. Понимаете, теперь так очевидно, что вы это говорите! Фактически, я мог бы также оставить ленивый экземпляр, но просто убедитесь, что я специально вызываю NSManagedObjectContext getter, чтобы отключить весь процесс установки и выбросить результат. Если я сделаю это до любых других основных данных, то все будет в порядке. Большое спасибо. –
Привет, Крейг, я не понял решение, которое вы можете объяснить более подробно, столкнувшись с подобной проблемой при синхронизации данных ядра, и пользователь попытается просмотреть конкретную запись, которая будет удалена. –
@iphonegeek. Это было вызвано двумя потоками, обращающимися к основному стеку данных при в то же время. Обычно это нормально, но в первый раз, когда он используется, существует обычный ленивый код-экземпляр, в котором говорится: 'if blah = nil then do alloc/init'. Проблема заключалась в том, что второй поток попал к методу, а ivars все еще отсутствовали поскольку alloc/init, выполняемый первым потоком, не был завершен. Решение заключалось в том, чтобы добавить следующую строку в основной поток перед тем, как развернуть второй поток: 'NSManagedObjectContext * unused __attribute __ ((unused)) = [self managedObjectContext];' Это гарантирует, что все будет создано. –