Основные данные - многопоточность - состояние гонки при запуске - PullRequest
4 голосов
/ 26 января 2012

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

1 Ответ

4 голосов
/ 26 января 2012

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

...