Для базовых данных существует золотое правило - один контекст управляемого объекта на поток. Контексты управляемых объектов не являются потокобезопасными, поэтому, если вы выполняете работу в фоновой задаче, вы либо используете основной поток, чтобы избежать конфликтов потоков с операциями пользовательского интерфейса, либо создаете новый контекст для выполнения работы. Если работа будет выполняться Через несколько секунд вы должны сделать это, чтобы остановить блокировку вашего интерфейса.
Для этого вы создаете новый контекст и сохраняете в нем то же постоянное хранилище, что и ваш основной контекст:
NSManagedObjectContext *backgroundContext = [[[NSManagedObjectContext alloc] init] autorelease];
[backgroundContext setPersistentStoreCoordinator:[mainContext persistentStoreCoordinator]];
Выполняйте все необходимые операции, затем при сохранении этого нового контекста вам нужно обработать уведомление о сохранении и объединить изменения с основным контекстом с сообщением mergeChangesFromContextDidSaveNotification:
. Код должен выглядеть примерно так:
/* Save notification handler for the background context */
- (void)backgroundContextDidSave:(NSNotification *)notification {
/* Make sure we're on the main thread when updating the main context */
if (![NSThread isMainThread]) {
[self performSelectorOnMainThread:@selector(backgroundContextDidSave:)
withObject:notification
waitUntilDone:NO];
return;
}
/* merge in the changes to the main context */
[self.managedObjectContext mergeChangesFromContextDidSaveNotification:notification];
}
/* ... */
/* Save the background context and handle the save notification */
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(backgroundContextDidSave:)
name:NSManagedObjectContextDidSaveNotification
object:backgroundContext];
[backgroundContext save:NULL];
[[NSNotificationCenter defaultCenter] removeObserver:self
name:NSManagedObjectContextDidSaveNotification
object:syncContext];
Обработка уведомлений о сохранении и слиянии важна, в противном случае ваш основной пользовательский интерфейс / контекст не увидит сделанные вами изменения. При слиянии ваш основной fetchResultsController и т. Д. Получит события изменений и обновит ваш пользовательский интерфейс, как вы ожидаете.
Еще одна важная вещь, которую следует отметить, - это то, что экземпляры NSManagedObject могут использоваться только в том контексте, из которого они были получены. Если вашей операции нужна ссылка на объект, вы должны передать объекту objectID
операции и повторно извлечь экземпляр NSManagedObject из нового контекста, используя existingObjectWithID:
. Так что-то вроде:
/* This can only be used in operations on the main context */
MyNSManagedObject *objectInMainContext =
[self.fetchedResultsController objectAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0]];
/* This can now be used in your background context */
MyNSManagedObject *objectInBackgroundContext =
(MyNSManagedObject *) [backgroundContext existingObjectWithID:[objectInMainContext objectID]];