У меня есть многопоточное приложение, которое использует 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
, поэтому создайте объект. Это вызывает проблемы для первого потока, поскольку ивар неожиданно указывает на неправильное место. В этот момент происходят плохие вещи!
Так как лучше это решить? Я временно исправил проблему, добавив sleep(1)
в фоновый поток :), но я не уверен, что это действительно лучшее решение! Я попытался изменить свойства NSPersistentStoreCoordinator
, чтобы они были атомарными, но это не помогло. Должен ли я обернуть каждый из стандартных геттеров в @synchronise
, чтобы заблокировать мьютекс? У меня еще не было времени попробовать это, но это должно остановить один и тот же код, выполняемый обоими потоками. Или есть лучший способ?
Мысли
Спасибо, Крейг
======================================
Для информации, вот некоторые из ошибок и сбоев, которые я видел. Они довольно случайны, но могут помочь другим найти этот пост в будущем.
2012-01-18 22: 19: 57.586 CBF [10468: 11d03] - [NSSQLModel
_addPersistentStore: identifier:]: нераспознанный селектор отправлен в экземпляр 0x6b80390
2012-01-18 22: 19: 57,595 CBF [10468: 11d03] * Завершение приложения из-за
необработанное исключение 'NSInvalidArgumentException', причина: '- [NSSQLModel
_addPersistentStore: identifier:]: нераспознанный селектор отправлен в экземпляр 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: identifier:]: нераспознанный селектор отправлен в экземпляр 0x6d59370
2012-01-25 21: 45: 31.175 CBF [16911: 1310b] * Завершение приложения из-за
необработанное исключение 'NSInvalidArgumentException', причина: '- [__ NSArrayM
_addPersistentStore: identifier:]: нераспознанный селектор отправлен в экземпляр 0x6d59370 '