Многопоточные данные ядра - NSManagedObject признан недействительным - PullRequest
1 голос
/ 26 июля 2011

Как видно из названия, я работаю с Базовым Приложением Данных, которое заполняется объектами в разных фоновых потоках (Синтаксический анализ XML)

В моей фоновой ветке я делаю это

managedContext = [[NSManagedObjectContext alloc] init];
[managedContext setUndoManager:nil];

[managedContext setPersistentStoreCoordinator: [[DataManager sharedManager] persistentStoreCoordinator]];

 NSNotificationCenter *nc = [NSNotificationCenter defaultCenter]; 
 [nc addObserver:self
        selector:@selector(mergeChanges:)
            name:NSManagedObjectContextDidSaveNotification
          object:managedContext];


NSMutableArray *returnSource = [[self parseDocument:doc] retain];


 [managedContext save:&error];

 if (error) {
     NSLog(@"saving error in datafeed"); 
 }

 [managedContext reset];

[self performSelectorOnMainThread:@selector(parseCompleteWithSource:) withObject:returnSource waitUntilDone:YES];

Метод слияния выглядит так:

NSManagedObjectContext *mainContext = [[DataManager sharedManager] managedObjectContext];

// Merge changes into the main context on the main thread
[mainContext performSelectorOnMainThread:@selector(mergeChangesFromContextDidSaveNotification:)
                              withObject:notification
                           waitUntilDone:YES];  

[[NSNotificationCenter defaultCenter] removeObserver:self];

Я думаю, что слияние прошло успешно, но, поскольку я хочу отобразить его в UITableView, оно всегда говорит мне, что мои объекты признаны недействительными, чего следует ожидать из-за

[managedContext reset];

То, что я хочу сделать, это показать элементы, которые в данный момент находятся в базе данных, в фоновом режиме проанализировать xml и, если это закончено, я хочу обновить UITableView новыми / обновленными объектами. Как мне это сделать, могу ли я как-то «обновить» объекты в другом контексте или слияние не работает правильно?

Нужно ли мне определять что-то конкретное в Main ObjectContext? Я попробовал разные политики слияния без какой-либо удачи.

Надеюсь, вы сможете мне помочь, спасибо!

Ответы [ 2 ]

3 голосов
/ 26 июля 2011

Я считаю, что ваша проблема - содержимое массива returnSource. Если это набор NSManagedObject экземпляров, то они будут созданы в фоновом потоке контекстом фонового потока.

Вы вызываете -[NSManagedObjectContext reset], что сделает их недействительными, поскольку вы явно указываете контексту делать это. Но это не большая проблема.

Затем вы отправляете массив в основной поток, передавая NSManagedObject экземпляров через границы потоков, и между контекстами получается большое нет-нет.

Что вам нужно сделать, это:

  1. Создать массив с NSManagedObjectID s из NSManagedObject.
  2. Отправка массива идентификаторов объектов через границы потока.
  3. Воссоздать массив с NSManagedObject s из идентификаторов управляемого объекта в новом потоке с его контекстом.

Я сделал несколько помощников Core Data, следуя правилу трех (в третий раз, когда вы что-то пишете, сделайте это общим) .

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

Обычный вариант использования:

NSManagedObjectCotext* context = [NSManagedObjectCotext threadLocalContext];
// Do your stuff!!
NSError* error = nil;
if (![context saveWithFailureOption:NSManagedObjectContextCWSaveFailureOptionThreadDefault 
                              error:&error]) 
{
    // Handle error.
}

Полный исходный код, включая пример приложения для разбора новостей RSS с apple.com и сохранения его в Core Data, доступен здесь: https://github.com/jayway/CWCoreData.

1 голос
/ 26 июля 2011

В этом случае нет причины вызывать reset в фоновом контексте, потому что он все равно исчезнет с фоновым потоком, и вы никогда не будете использовать его снова в любом случае.Обычно вы используете reset с управлением отмены, когда вы хотите, чтобы контекст забыл предыдущие шаги.

Ваша основная проблема здесь в том, что ваш фоновый поток настроен на получение уведомлений из контекста управляемого объекта, который он создает.Это довольно бессмысленно.

Вместо этого вам нужно зарегистрировать контроллер табличного представления (в переднем потоке), чтобы получить NSManagedObjectContextDidSaveNotification из контекста в фоновом потоке.Таким образом, при сохранении фонового контекста передний / главный контекст обновляется новыми данными, что вызывает обновление табличного представления.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...