通过外部线程保存一堆 NSManagedObjects 时遇到冲突。对于初学者,我可以告诉您以下内容:
- 我为每个线程使用单独的 MOC。
- MOC 共享相同的持久存储协调器。
- 外部线程可能正在修改我正在保存的一条或多条记录。
好吧,既然这样,这就是我正在做的事情。
在我的外部线程中,我正在执行一些计算并更新一堆托管对象中的单个值。为此,我通过主键在持久存储中查找对象,修改单个十进制属性,然后一次调用所有对象的 save。
与此同时,我相信主线程正在做一些自己的更新。
当我的外部线程对其托管对象上下文进行大量保存时,我会抛出一个异常,指出存在大量冲突。所有冲突似乎都集中在每条记录上的单一关系上。尽管持久存储中的托管对象和我的外部线程在这种关系中共享相同的 ObjectID,但它们不共享相同的指针。根据我所看到的,这是我的 NSMergeConflict 调试输出中的对象之间唯一不同的地方。
对我来说,为什么这两个对象与不同的指针有关系是有道理的——它们位于不同的线程中。然而,据我从 Apple 文档中了解到,首次从持久存储中检索对象时唯一缓存的是全局 ID。因此,人们会认为当我在外部线程 MOC 上运行 save 时,它会比较 ObjectID,发现它们是相同的,然后让一切通过。
那么,谁能告诉我为什么会发生冲突?
I'm having a conflict when saving a bunch of NSManagedObjects via an outside thread. For starters, I can tell you the following:
- I'm using a separate MOC for each thread.
- The MOCs share the same persistent store coordinator.
- It's likely that an outside thread is modifying one or many of the records that I'm saving.
OK, so with that out of the way, here's what I'm doing.
In my outside thread, I'm doing some computation and updating a single value in a bunch of managed objects. I do this by looking up the object in the persistent store by my primary key, modifying the single decimal property, and then calling save on the bunch all at once.
In the meantime, I believe the main thread is doing some updating of its own.
When my outside thread does its big save on its managed object context, I get an exception thrown stating a large number of conflicts. All of the conflicts seem to be centered around a single relationship on each record. Though the managed object in the persistent store and my outside thread share the same ObjectID for this relationship, they don't share the same pointer. Based on what I see, that's the only thing that's different between the objects in my NSMergeConflict debug output.
It makes sense to me why the two objects have relationships with different pointers -- they're in different threads. However, as I understand it from Apple's documentation, the only thing cached when an object is first retrieved from the persistent store are the global IDs. So, one would think that when I run save on the outside thread MOC, it compares the ObjectIDs, sees they're the same, and lets it all through.
So, can anyone tell me why I'm getting a conflict?
发布评论
评论(3)
根据核心数据编程指南的与核心数据的并发章节中的文档,建议的配置是上下文共享相同的持久存储协调器,而不仅仅是相同的持久存储。
此外,同一章的“使用通知跟踪其他线程中的更改”部分指出,如果您使用 NSManagedObjectContextDidSaveNotification 跟踪更新,则发送 -mergeChangesFromContextDidSaveNotification 到主线程的上下文,以便它可以合并更改。但是,如果您使用 NSManagedObjectContextDidChangeNotification 进行跟踪,则外部线程应将已修改对象的对象 ID 发送到主线程,然后主线程将 -refreshObject:mergeChanges: 发送到其每个修改对象的上下文。
实际上,您应该知道主线程是否也通过其控制器执行更新,并以类似的方式但以相反的方向传播其更改。
Per the documentation in the Concurrency with Core Data chapter of The Core Data Programming Guide, the recommended configuration is for the contexts to share the same persistent store coordinator, not just the same persistent store.
Also, the section Track Changes in Other Threads Using Notifications of the same chapter states if you're tracking updates with the NSManagedObjectContextDidSaveNotification then you send -mergeChangesFromContextDidSaveNotification to the main thread's context so it can merge the changes. But if you're tracking with NSManagedObjectContextDidChangeNotification then the external thread should send the object IDs of the modified objects to the main thread which will then send -refreshObject:mergeChanges: to its context for each modified object.
And really, you should know if the main thread is also performing updates through its controller, and propagate its changes in like manner but in the opposite direction.
您需要让所有上下文从任何进行更改的上下文中侦听 NSManagedObjectContextDidSaveNotification。否则,只有前端上下文会知道后台线程上所做的更改,但后台上下文不会知道前端线程上的更改。
因此,如果您有三个线程和三个上下文,每个线程都会进行更改,则所有三个上下文都必须注册来自其他两个的通知。
You need to have all your contexts listening for NSManagedObjectContextDidSaveNotification from any context that makes changes. Otherwise, only the front context will be aware of changes made on the background threads but the background context won't be aware of changes on the front thread.
So, if you have three threads and three context each of which makes changes, all three context must register for notifications from the other two.
不幸的是,这个错误似乎实际上是由其他原因引起的——我在不应该调用的情况下多次调用导致错误的操作。尽管这并没有回答关于为什么指针在冲突中很重要的最初问题,但更新我的代码以防止这种情况已经解决了我的问题。
Unfortunately, it seems as though this bug was actually being caused by something else -- I was calling the operation causing the error more than once at the same time when I shouldn't have been. Although this doesn't answer the initial question as to why pointers matter in conflicts, updating my code to prevent this situation has resolved my issue.