4

У меня есть многопоточное приложение, которое использует 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'

ответ

4

Почему не только удалить ленивый экземпляр, а вместо этого создать координатора сразу после запуска приложения?

+1

Поблагодарить Пол. Понимаете, теперь так очевидно, что вы это говорите! Фактически, я мог бы также оставить ленивый экземпляр, но просто убедитесь, что я специально вызываю NSManagedObjectContext getter, чтобы отключить весь процесс установки и выбросить результат. Если я сделаю это до любых других основных данных, то все будет в порядке. Большое спасибо. –

+0

Привет, Крейг, я не понял решение, которое вы можете объяснить более подробно, столкнувшись с подобной проблемой при синхронизации данных ядра, и пользователь попытается просмотреть конкретную запись, которая будет удалена. –

+1

@iphonegeek. Это было вызвано двумя потоками, обращающимися к основному стеку данных при в то же время. Обычно это нормально, но в первый раз, когда он используется, существует обычный ленивый код-экземпляр, в котором говорится: 'if blah = nil then do alloc/init'. Проблема заключалась в том, что второй поток попал к методу, а ivars все еще отсутствовали поскольку alloc/init, выполняемый первым потоком, не был завершен. Решение заключалось в том, чтобы добавить следующую строку в основной поток перед тем, как развернуть второй поток: 'NSManagedObjectContext * unused __attribute __ ((unused)) = [self managedObjectContext];' Это гарантирует, что все будет создано. –

Смежные вопросы