我想在主线程上更改核心数据对象,然后保存在后台线程中。后台线程保存是否包括主线程更改?
4 回答
您可以以多线程方式使用 Core Data,但您应该遵循Apple 推荐的方法之一:
Core Data 并发编程的推荐模式是线程限制:每个线程必须有自己的完全私有的托管对象上下文。
采用该模式有两种可能的方式:
为每个线程创建一个单独的托管对象上下文并共享一个持久存储协调器。这是通常推荐的方法。
为每个线程创建一个单独的托管对象上下文和持久存储协调器。这种方法以更大的复杂性(特别是如果您需要在不同的上下文之间传达更改)和增加的内存使用量为代价提供更大的并发性。
特别:
使用线程限制,您不应在线程之间传递托管对象或托管对象上下文。要跨线程边界从一个上下文“传递”另一个托管对象,您可以:
传递其对象 ID (objectID) 并在接收托管对象上下文中使用 objectWithID: 或 existingObjectWithID:error:。必须保存相应的托管对象——您不能将新插入的托管对象的 ID 传递给另一个上下文。
在接收上下文上执行 fetch。这些在接收上下文中创建托管对象的本地版本。
由此,您无法在线程中创建托管对象(具有自己的上下文)然后将它们保存在另一个线程中。
所以,为了完成你想要的,你需要在线程之间共享一个托管对象上下文或一个持久存储协调器。在这种情况下,您应该正确使用锁定技术来防止您的商店进入不一致的状态:
如果您在线程之间共享托管对象上下文或持久存储协调器,则必须确保任何方法调用都是从线程安全范围进行的。对于锁定,您应该在托管对象上下文和持久存储协调器上使用 NSLocking 方法,而不是实现自己的互斥锁。这些方法有助于向框架提供有关应用程序意图的上下文信息——也就是说,除了提供互斥体之外,它们还有助于确定操作集群的范围。
如果您有机会修改应用程序的设计,以便您不需要为核心数据对象进行锁定和同步,我不建议您走这条路。
您要求的内容适合嵌套上下文。您创建一个私有队列上下文,并将其直接附加到持久存储协调器。很简单。拿你当前的代码,而不是这个......
managedObjectContext = [[NSManagedObjectContext alloc] init];
用这个代替它...
workerManagedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
现在,您已经用一个新的 MOC 替换了传统的限制 MOC,该 MOC 将使用自己的并发队列运行。
要从主线程中获取可以使用的上下文,您需要创建另一个托管对象上下文,并使其成为您刚刚创建的上下文的子...
managedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
managedObjectContext.parent = workerManagedObjectContext;
这意味着您可以像以前一样使用 managedObjectContext。然而,现在,它不是直接进入商店,而是通过中间上下文。父上下文将在其自己的后台线程中执行工作。
因此,您可以根据需要对 managedObjectContext 进行所有更改。当需要保存时,您可以执行以下操作...
static void saveManagedObjectContext(NSManagedObjectContext *moc, void(^completionBlock)(NSError *error)) {
[moc performBlock:^{
NSError *error = nil;
if (moc.hasChanges && [moc save:&error] && moc.parentContext) {
saveManagedObjectContext(moc.parentContext, completionBlock);
} else {
completionBlock(error);
}
}];
}
编辑
如果您想普遍使用它,您可以轻松地将其添加到 NSManagedObjectContext 上的类别中,然后只需调用...
[managedObjectContext saveWithCompletionBlock:^(NSError *error){
if (error) {
// Handle the error
return;
}
// Handle success...
}];
Apple 建议为单独的线程使用单独的上下文。一旦你调用了 save ,它的上下文状态就会被保存到实际的数据库中。如果您需要将主线程更改反映在后台线程中,请将后台线程上下文与主线程上下文合并并调用保存。