当他们说一个NSManagedObjectContext被创build它的线程或队列拥有时,苹果是什么意思?
看起来在11月份,苹果公司更新了NSManagedObjectContext类参考和核心数据编程指南文档,明确祝福串行GCD调度队列和NSOperationQueues作为同步访问NSManagedObjectContext
可接受机制。 但是他们的build议似乎是模棱两可的,可能是矛盾的,我想确保我已经正确地理解了。
以前,人们公认的NSManagedObjectContext
是NSManagedObjectContext
只能从创build它的线程访问,而使用串行队列进行同步是不够的; 尽pipe串行队列一次只能执行一个操作,但是这些操作可能会在不同的线程上进行调度,而且MOC不这样做。
但现在,从节目指南,我们有:
您可以使用线程,串行操作队列或分派队列进行并发。 为了简洁起见,本文使用“线程”来指代其中的任何一个。
到目前为止,这么好(虽然他们的线程和队列的合并是无益的)。 所以我可以安全地使用每个(串行)队列的单个上下文,而不是每个操作/块一个,对不对? 苹果甚至在核心数据WWDC会议中对此进行了视觉描述。
但是…你在哪里创build队列的上下文? 在NSManagedObjectContext
文档中,Apple声明:
[上下文]假定默认所有者是分配它的线程或队列 – 这是由调用其init方法的线程决定的。 因此,您不应该在一个线程上初始化上下文,然后将其传递给另一个线程。
所以现在我们有一个NSManagedObjectContext
的想法需要知道谁是它的所有者。 我假设这意味着要在队列中执行的第一个操作应该创buildMOC并保存对其余操作的引用。
这是正确的吗? 我犹豫的唯一原因是NSManagedObjectContext
文章继续说:
相反,您应该将一个引用传递给一个持久性存储协调器,并让接收线程/队列创build一个由此产生的新的上下文。 如果使用NSOperation,则必须在main(对于串行队列)或start(对于并发队列)中创build上下文。
现在,苹果似乎正在将运营与排期执行的队列进行混合。 这是我的头脑,让我怀疑他们是否真的希望你为每一个操作创build一个新的商务部。 我错过了什么?
NSManagedObjectContext和任何与之关联的托pipe对象应该被固定到一个actor(线程,序列化队列,NSOperationQueue,max concurrency = 1)。
这种模式被称为线程限制或隔离。 对于(线程||序列化队列|| NSOperationQueue,max concurrency = 1)没有一个很好的说法,所以文档继续说:“我们只是使用'线程'的核心数据文档的其余部分,当我们的意思这三种获得序列化控制stream的方法中的任何一种“
如果您在一个线程上创buildMOC,然后在另一个线程上使用它,则通过将MOC对象引用暴露给两个线程来违反了线程限制。 简单。 不要这样做。 不要过河。
我们明确地调用NSOperation,因为不像线程和GCD,它有这个奇怪的问题,其中-init在创buildNSOperation的线程上运行,而-main在运行NSOperation的线程上运行。 如果你正确的眯起来,这是有道理的,但这并不直观。 如果你在 – [NSOperation init]中创build了MOC,那么在你的-main方法运行之前,NSOperation将有助于违反线程限制。
我们积极劝阻/不赞成使用MOC和线程以任何其他方式。 虽然理论上可以做什么bbum提及,没有人得到这个权利。 每个人都绊倒了,忘记了一个必要的电话 – 在1个地方locking,“init运行在哪里?”,否则就会自我克制。 使用自动释放池和应用程序事件循环以及撤消pipe理器和cocoa绑定以及KVO,在尝试将其传递到其他地方之后,一个线程可以坚持对MOC的引用。 要比先进的Cocoa开发人员想象的要困难得多,直到他们开始debugging。 所以这不是一个非常有用的API。
文件改变,澄清和强调线程禁闭模式是唯一的理智的方式去。 你应该考虑尝试使用NSManagedObjectContext上的-lock和-unlock(a)是不可能的,以及(b)事实上的弃用。 这不是从字面上不推荐使用,因为代码和以前一样工作。 但是你的代码使用它是错误的。
有些人在1个线程上创buildMOC,并将其传递给另一个线程而不调用-lock。 那从来都不合法。 创build商务部的线程一直是商务部的默认所有者。 对于在主线程上创build的MOC,这成为了更常见的问题。 主线程MOC与应用程序的主事件循环进行交互,以进行撤消,内存pipe理和其他一些原因。 在10.6和iOS 3上,MOC更主动地占据主线程的优势。
虽然队列没有绑定到特定的线程,但是如果在队列的上下文中创build一个MOC,就会发生正确的事情。 您的义务是遵循公共API。
如果队列被序列化,则可以与在该队列上运行的后续块共享MOC。
所以在任何情况下都不要将NSManagedObjectContext *暴露给多个线程(actor等)。 有一个模棱两可的问题。 您可以将didSave通知中的NSNotification *传递给另一个线程的MOC的-mergeChangesFromContextDidSaveNotification:方法。
- 本
听起来你是对的。 如果你使用线程,需要上下文的线程需要创build它。 如果您使用的是队列,那么需要上下文的队列应该创build它,很可能是队列中第一个要执行的块。 这听起来似乎是唯一令人困惑的部分是有关NSOperations的一点。 我认为NSOperations并没有提供任何底层线程/队列的保证,所以即使它们都在同一个NSOperationQueue上运行,在操作之间共享一个MOC也是不安全的。 另一种解释是,这只是令人困惑的文档。
把它们加起来:
- 如果您使用线程,请在需要的线程上创buildMOC
- 如果您正在使用GCD,请在串行队列上执行的第一个块中创buildMOC
- 如果您使用的是NSOperation,请在NSOperation内部创buildMOC,不要在操作之间共享它。 这可能有点偏执,但是NSOperation并不能保证它运行的底层线程/队列。
编辑 :根据bbum,唯一真正的要求是访问需要序列化。 这意味着只要操作全部添加到同一个队列,并且队列不允许并发操作,就可以跨NS操作共享一个MOC。