用于保存核心数据的NSPersistentContainer并发性

我已经阅读了一些关于这个的博客,但是我仍然对如何使用NSPersistentContainer performBackgroundTask创build实体并保存它感到困惑。 通过在performBackgroundTask() { (moc) in }块中调用便利方法init(context moc: NSManagedObjectContext)创build一个实例后,如果我检查container.viewContext.hasChanges这将返回false,并说没有什么可以保存,如果我调用保存moc (为这个块创build的背景MOC)我得到像这样的错误:

 fatal error: Failure to save context: Error Domain=NSCocoaErrorDomain Code=133020 "Could not merge changes." UserInfo={conflictList=( "NSMergeConflict (0x17466c500) for NSManagedObject (0x1702cd3c0) with objectID '0xd000000000100000 <x-coredata://3EE6E11B-1901-47B5-9931-3C95D6513974/Currency/p4>' with oldVersion = 1 and newVersion = 2 and old cached row = {id = 2; ... }fatal error: Failure to save context: Error Domain=NSCocoaErrorDomain Code=133020 "Could not merge changes." UserInfo={conflictList=( "NSMergeConflict (0x170664b80) for NSManagedObject (0x1742cb980) with objectID '0xd000000000100000 <x-coredata://3EE6E11B-1901-47B5-9931-3C95D6513974/Currency/p4>' with oldVersion = 1 and newVersion = 2 and old cached row = {id = 2; ...} and new database row = {id = 2; ...}" )} 

所以我没有得到并发性的工作,真的很感激,如果有人可以向我解释在iOS 10核心数据上使用此function的正确方法

TL:DR :你的问题是你正在使用viewContext和背景上下文。 你只能以同步的方式写入核心数据。

完整的解释:如果一个对象同时从两个不同的上下文中改变,core-data不知道该怎么做。 您可以设置mergePolicy来设置哪个更改应该赢,但这不是一个好的解决scheme,因为您可能会以这种方式丢失数据。 很多专业人士长期以来一直在处理这个问题的方法是有一个操作队列来排队这些写操作,因此一次只有一个写操作正在进行,并且在主线程上只有另一个上下文用于读。 这样你永远不会得到任何合并冲突。 (有关此设置的详细说明,请参阅https://vimeo.com/89370886 )。

使用NSPersistentContainer设置非常简单。 在你的核心数据pipe理器中创build一个NSOperationQueue

 _persistentContainerQueue = [[NSOperationQueue alloc] init]; _persistentContainerQueue.maxConcurrentOperationCount = 1; 

并使用这个队列做所有的写作:

 - (void)enqueueCoreDataBlock:(void (^)(NSManagedObjectContext* context))block{ void (^blockCopy)(NSManagedObjectContext*) = [block copy]; [self.persistentContainerQueue addOperation:[NSBlockOperation blockOperationWithBlock:^{ NSManagedObjectContext* context = self.persistentContainer.newBackgroundContext; [context performBlockAndWait:^{ blockCopy(context); [context save:NULL]; //Don't just pass NULL here. look at the error and log it to your analytics service }]; }]]; } 

当您调用enqueueCoreDataBlock该块将被排队以确保没有合并冲突。 但是,如果你写的viewContext会击败这个设置。 同样,您应该将您创build的任何其他上下文(使用newBackgroundContextnewBackgroundContext )视为只读,因为它们也将在写入队列之外。

起初我以为NSPersistentContainerperformBackgroundTask有一个内部队列,并且初始testing支持。 经过更多的testing,我发现它也可能导致合并冲突。