当 Apple 说 NSManagedObjectContext 归创建它的线程或队列所有时,Apple 是什么意思? [英] What does Apple mean when they say that a NSManagedObjectContext is owned by the thread or queue that created it?

查看:19
本文介绍了当 Apple 说 NSManagedObjectContext 归创建它的线程或队列所有时,Apple 是什么意思?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

似乎在 11 月,Apple 更新了 NSManagedObjectContext 类参考核心数据编程指南 文档明确祝福串行 GCD 调度队列和 NSOperationQueues 作为可接受的机制同步访问 NSManagedObjectContext.但他们的建议似乎模棱两可,而且可能相互矛盾,我想确保我已经正确理解了.

It seems that in November, Apple updated both the NSManagedObjectContext Class Reference and the Core Data Programming Guide documents to explicitly bless serial GCD Dispatch Queues and NSOperationQueues as acceptable mechanisms for synchronising access to a NSManagedObjectContext. But their advice seems ambiguous and possibly contradictory, and I want to make sure I've understood it properly.

以前公认的观点似乎是 NSManagedObjectContext 只能从创建它的线程访问,并且使用串行队列进行同步是不够的;尽管串行队列一次只执行一个操作,但这些操作可能会被调度到不同的线程上,而 MOC 不喜欢这样.

Previously the accepted wisdom seemed to be that a NSManagedObjectContext could only be accessed from the thread that created it, and that using a serial queue for synchronisation was not sufficient; although serial queues only perform one operation at a time, these operations can potentially be scheduled on different threads, and a MOC doesn't like that.

但是现在,根据编程指南,我们有:

But now, from the programming guide, we have:

您可以使用线程、串行操作队列或调度队列来实现并发.为简洁起见,本文通篇使用线程"来指代其中任何一个.

You can use threads, serial operation queues, or dispatch queues for concurrency. For the sake of conciseness, this article uses "thread" throughout to refer to any of these.

到目前为止,一切都很好(尽管它们将线程和队列混为一谈并没有帮助).所以我可以安全地为每个(串行)队列使用一个上下文,而不是每个操作/块一个,对吗?Apple 甚至在 Core Data WWDC 会议中对此进行了直观的描述.

So far, so good (although their conflation of threads and queues is unhelpful). So I can safely use a single context per (serial) queue, instead of one per operation/block, right? Apple even has a visual depiction of this in the Core Data WWDC sessions.

但是...你在哪里为队列创建上下文?在 NSManagedObjectContext 文档中,Apple 声明:

But... where do you create the context for the queue? In the NSManagedObjectContext documentation, Apple state:

[A context] 假定默认所有者是分配它的线程或队列——这由调用其 init 方法的线程决定.因此,您不应在一个线程上初始化上下文,然后将其传递给另一个线程.

[A context] assumes the default owner is the thread or queue that allocated it—this is determined by the thread that calls its init method. You should not, therefore, initialize a context on one thread then pass it to a different thread.

所以现在我们有了一个NSManagedObjectContext 需要知道它的所有者是谁的想法.我假设这意味着要在队列中执行的第一个操作应该创建 MOC 并保存对它的引用以供其余操作使用.

So now we have the idea of a NSManagedObjectContext needing to know who its owner is. I'm assuming this means that the first operation to be executed in the queue should create the MOC and save a reference to it for the remaining operations to use.

这样对吗?我犹豫的唯一原因是 NSManagedObjectContext 文章接着说:

Is this right? The only reason I'm hesitant is that the NSManagedObjectContext article goes on to say:

相反,您应该传递一个对持久存储协调器的引用,并让接收线程/队列创建一个从中派生的新上下文.如果使用 NSOperation,则必须在 main(对于串行队列)或 start(对于并发队列)中创建上下文.

Instead, you should pass a reference to a persistent store coordinator and have the receiving thread/queue create a new context derived from that. If you use NSOperation, you must create the context in main (for a serial queue) or start (for a concurrent queue).

Apple 现在似乎将操作与调度执行的队列混为一谈.这引起了我的注意,让我怀疑他们是否真的希望你为每个操作创建一个新的 MOC.我错过了什么?

Apple now seem to be conflating operations with the queues that schedule their execution. This does my head in, and makes me wonder if they really do want you to just create a new MOC for every operation after all. What am I missing?

推荐答案

NSManagedObjectContext 和与之关联的任何托管对象都应该固定到单个 actor(线程、序列化队列、最大并发数为 1 的 NSOperationQueue).

The NSManagedObjectContext and any managed objects associated with it should be pinned to a single actor (thread, serialized queue, NSOperationQueue with max concurrency = 1).

这种模式称为线程限制或隔离.对于 (thread || serialized queue || NSOperationQueue with max concurrency = 1) 没有什么好用语,所以文档继续说我们将在 Core Data 文档的其余部分使用‘线程’,当我们的意思是获得序列化控制流的 3 种方法中的任何一种"

This pattern is called thread confinement or isolation. There isn't a great phrase for (thread || serialized queue || NSOperationQueue with max concurrency = 1) so the documentation goes on to say "we'll just use 'thread' for the remainder of the Core Data doc when we mean any of those 3 ways of getting a serialized control flow"

如果您在一个线程上创建一个 MOC,然后在另一个线程上使用它,那么您将 MOC 对象引用暴露给两个线程,从而违反了线程限制.简单的.不要这样做.不要越过溪流.

If you create a MOC on one thread, and then use it on another, you have violated thread confinement by exposing the MOC object reference to two threads. Simple. Don't do it. Don't cross the streams.

我们明确调用 NSOperation 因为不像线程 &GCD,它有一个奇怪的问题,-init 在创建 NSOperation 的线程上运行,而 -main 在运行 NSOperation 的线程上运行.如果你斜视它是有道理的,但它并不直观.如果你在 -[NSOperation init] 中创建你的 MOC,那么 NSOperation 将在你的 -main 方法甚至运行之前帮助违反线程限制并且你被灌输.

We call out NSOperation explicitly because unlike threads & GCD, it has this odd issue where -init runs on the thread creating the NSOperation but -main runs on the thread running the NSOperation. It makes sense if you squint at it right, but it is not intuitive. If you create your MOC in -[NSOperation init], then NSOperation will helpfully violate thread confinement before your -main method even runs and you're hosed.

我们积极阻止/弃用以任何其他方式使用 MOC 和线程.虽然理论上可以做 bbum 提到的事情,但没有人做对了.每个人都被绊倒了,忘记了在 1 个地方对 -lock 的必要调用,init 运行在哪里?",或者以其他方式超越自己.使用自动释放池和应用程序事件循环以及撤消管理器和可可绑定以及 KVO,在您尝试将 MOC 传递到其他地方后,一个线程可以通过多种方式保持对 MOC 的引用.在开始调试之前,即使是高级 Cocoa 开发人员也比他们想象的要困难得多.所以这不是一个非常有用的 API.

We actively discourage / deprecated using MOCs and threads in any other ways. While theoretically possible to do what bbum mentions, no one ever got that right. Everybody tripped up, forgot a necessary call to -lock in 1 place, "init runs where ?", or otherwise out-clevered themselves. With autorelease pools and the application event loop and the undo manager and cocoa bindings and KVO there are just so many ways for one thread to hold on to a reference to a MOC after you've tried to pass it elsewhere. It is far more difficult than even advanced Cocoa developers imagine until they start debugging. So that's not a very useful API.

文档已更改以澄清和强调线程限制模式是唯一明智的方法.您应该考虑尝试在 NSManagedObjectContext 上使用 -lock 和 -unlock 变得更加花哨,以 (a) 不可能和 (b) 实际上已弃用.它实际上并没有被弃用,因为代码和以前一样有效.但是您使用它的代码是错误的.

The documentation changed to clarify and emphasize the thread confinement pattern as the only sane way to go. You should consider trying to be extra fancy using -lock and -unlock on NSManagedObjectContext to be (a) impossible and (b) de facto deprecated. It's not literally deprecated because the code works as well as it ever did. But your code using it is wrong.

有些人在 1 个线程上创建 MOC,然后将它们传递给另一个线程而不调用 -lock.那从来都不合法.创建 MOC 的线程一直是 MOC 的默认所有者.对于在主线程上创建的 MOC,这成为一个更常见的问题.主线程 MOC 与应用程序的主事件循环交互,用于撤消、内存管理和其他一些原因.在 10.6 和 iOS 3 上,MOC 更积极地利用主线程拥有的优势.

Some people created MOCs on 1 thread, and passed them to another without calling -lock. That was never legal. The thread that created the MOC has always been the default owner of the MOC. This became a more frequent issue for MOCs created on the main thread. Main thread MOCs interact with the application's main event loop for undo, memory management, and some other reasons. On 10.6 and iOS 3, MOCs take more aggressive advantage of being owned by the main thread.

虽然队列不绑定到特定的线程,但如果你在队列的上下文中创建一个 MOC,正确的事情就会发生.您的义务是遵循公共 API.

Although queues are not bound to specific threads, if you create a MOC within the context of a queue the right things will happen. Your obligation is to follow the public API.

如果队列是序列化的,您可以与在该队列上运行的后续块共享 MOC.

If the queue is serialized, you may share the MOC with succeeding blocks that run on that queue.

因此,在任何情况下都不要将 NSManagedObjectContext* 暴露给多个线程(actor 等).有一个歧义.您可以将来自 didSave 通知的 NSNotification* 传递给另一个线程的 MOC 的 -mergeChangesFromContextDidSaveNotification: 方法.

So do not expose an NSManagedObjectContext* to more than one thread (actor, etc) under any circumstance. There is one ambiguity. You may pass the NSNotification* from the didSave notification to another thread's MOC's -mergeChangesFromContextDidSaveNotification: method.

这篇关于当 Apple 说 NSManagedObjectContext 归创建它的线程或队列所有时,Apple 是什么意思?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆