用于保存核心数据的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的任何其他上下文(使用newBackgroundContext
或newBackgroundContext
)视为只读,因为它们也将在写入队列之外。
起初我以为NSPersistentContainer
的performBackgroundTask
有一个内部队列,并且初始testing支持。 经过更多的testing,我发现它也可能导致合并冲突。
- NSOperationQueue mainQueue vs performSelectorOnMainThread?
- dispatch_once过度杀伤了+ ?
- 在不同的线程上调用我的方法有哪些不同的方法?
- iOS GCD自定义并发队列执行顺序
- 如何找出是什么导致IOS设备上的错误崩溃报告?
- 核心数据:儿童上下文是否获得新插入对象的永久对象ID?
- 在iOS 7上使用Private Queue Deadlocks Parent的子级上下文执行块locking和等待
- iOS GCD:任何全局队列和具有后台优先级(DISPATCH_QUEUE_PRIORITY_BACKGROUND)之间的区别?
- 为什么我应该为每个新线程或NSOperation创build一个NSManagedObjectContext,而不是在主线程上调用Core Data?