现在让我们忽略 MagicalRecord。要在多个线程上使用 Core Data,您需要注意一些事项。
NSManagedObject
永远不要在线程之间传递。相反,传递NSManagedObjectID
你想要的对象,然后在你的后台线程中重新获取它。
- 负责任地构建您的
NSManagedObjectContext
. 这意味着你必须知道什么initWithConcurrencyType:
意思。我们将深入探讨。
主线程
您的 mainNSManagedObjectContext
应该使用并发类型构建NSMainQueueConcurrencyType
。这将允许您利用队列中内置的上下文来执行串行操作。每当后台线程与主上下文交互时,您都应该使用performBlock
or来完成工作performBlockAndWait
。
- (NSManagedObjectContext *)managedObjectContext
{
if (_managedObjectContext != nil) {
return _managedObjectContext;
}
NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
if (coordinator != nil) {
_managedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
[_managedObjectContext setPersistentStoreCoordinator:coordinator];
}
return _managedObjectContext;
}
后台线程
每当您在后台线程中工作时,您都需要启动一个新的上下文。您不应该在线程之间共享上下文。将对您的主线程上下文的引用传递到您的操作中,并在您的操作开始后构建一个后台上下文。这将确保它建立在您将执行工作的线程上。
- (NSManagedObjectContext *)newBackgroundManagedObjectContext
{
// Create new context with private concurrency type
NSManagedObjectContext *newContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
[newContext setParentContext:self.mainContext];
// Optimization
[newContext setUndoManager:nil];
return newContext;
}
将此背景上下文视为便笺簿。无论您在那里做什么,都会一直留在那里,直到您保存为止。因为您设置了 parentContext,所以在后台上下文中保存会将更改合并到您的主上下文中。这将更新NSFetchedResultsController
,但由于您尚未调用保存,因此数据尚未持久化。在您的队列 KVO 中,您可以通过排队保存块来调用主上下文的保存。
[self performBlock:^{
NSError *error;
[self save:&error];
if (error) {
// handle errors
}
}];