对于核心数据,有一条黄金法则——每个线程一个托管对象上下文。托管对象上下文不是线程安全的,因此如果您在后台任务中工作,您可以使用主线程来避免与 UI 操作的线程冲突,或者创建一个新上下文来完成工作。如果工作需要几秒钟后,您应该执行后者以阻止您的 UI 锁定。
为此,您创建一个新上下文并为其提供与主上下文相同的持久存储:
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];
处理保存通知和合并很重要,否则您的主 UI/上下文将看不到您所做的更改。通过合并,您的主要 fetchResultsController 等将获得更改事件并按照您的预期更新您的 UI。
另一个需要注意的重要事情是 NSManagedObject 实例只能在从中获取它们的上下文中使用。如果您的操作需要对对象的引用,那么您必须将对象传递objectID
给操作并使用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]];