似乎在11月,苹果更新了NSManagedObjectContext Class ReferenceCore Data Programming Guide文档,明确支持串行GCD调度队列和NSOperationQueues作为同步访问NSManagedObjectContext的可接受机制.但他们的建议似乎模棱两可,可能相互矛盾,我想确保我已经正确理解了.

以前,人们普遍认为NSManagedObjectContext只能从创建它的线程访问,使用串行队列进行同步是不够的;虽然串行队列一次只执行一个操作,但这些操作可能会安排在不同的线程上,MOC不喜欢这样.

但现在,从编程指南中,我们有:

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

到目前为止,一切都很好(尽管它们将线程和队列混为一谈无济于事).所以我可以安全地在每个(串行)队列中使用一个上下文,而不是在每个操作/块中使用一个上下文,对吗?苹果甚至在核心数据WWDC会议上对这一点进行了可视化描述.

但是在哪里为队列创建上下文?在NSManagedObjectContext份文件中,苹果声明:

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

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

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

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

苹果现在似乎把操作和安排执行的队列混为一谈.这让我很惊讶,我想知道他们是否真的希望你为每一个操作创建一个新的主运行中心.我错过了什么?

推荐答案

NSManagedObjectContext及其关联的任何托管对象都应固定到单个参与者(线程、序列化队列、NSOperationQueue with max concurrency=1).

这种模式称为线程限制或隔离.对于(线程| |序列化队列| | | NSOperationQueue with max concurrency=1)没有一个很好的短语,所以文档接着说,"当我们指的是获得序列化控制流的三种方法中的任何一种时,我们将对核心数据文档的其余部分使用‘线程’."

如果在一个线程上创建MOC,然后在另一个线程上使用它,那么通过将MOC对象引用expose 给两个线程,就违反了线程限制.易于理解的不要这样做.不要越过小溪.

我们显式地调用NSOperation,因为不像threads&;GCD,它有一个奇怪的问题,即-init在创建NSOperation的线程上运行,而-main在运行NSOperation的线程上运行.如果你正确地斜视它,这是有道理的,但这不是直觉.如果您在-[NSOperation init]中创建了MOC,那么在-main方法运行之前,NSOperation将有助于违反线程限制,从而使您受到限制.

我们积极劝阻/反对以任何其他方式使用MOC和线程.虽然理论上可以做到bbum提到的事情,但没有人能做到这一点.每个人都绊倒了,忘记了在一个地方打一个必要的电话来锁定,"init runs where?",或者以其他方式超越自己.有了自动释放池、应用程序事件循环、undo manager、cocoa绑定和KVO,一个线程在try 将MOC的引用传递到其他地方后,可以通过多种方式保持对MOC的引用.在高级Cocoa开发人员开始调试之前,这远比他们想象的困难.所以这不是一个非常有用的API.

文件更改为澄清并强调线程限制模式是唯一明智的 Select .你应该考虑在NSMARKEDIT对象上下文中使用锁和解锁来try 额外的幻想,这是不可能的,(b)事实上被贬低了.它并没有因为代码运行得和以前一样好而被弃用.但是你使用它的代码是错误的.

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

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

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

因此,在任何情况下,都不要将NSManagedObjectContext*expose 给多个线程(actor等).有一点模棱两可.您可以将NSNotification*从didSave通知传递到另一个线程的MOC-mergeChangesFromContextDidSaveNotification:方法.

Objective-c相关问答推荐

我应该为 memcpy 和 realloc 包含什么标题?

在@implementation 而不是@interface 定义的类变量?

获取按其各自值排序的 NSDictionary 键

在可可中的字符串上使用 MD5 哈希?

XCTAssert 和 XCTAssertTrue 有什么区别?

如何处理 Xcode 警告没有以前的函数原型......?

改变 setContentOffset:animated: 的速度?

MKMapView MKPointAnnotation 点击​​事件

当单元格全屏时,为什么 UICollectionView 会记录错误?

关于如何将项目从 UITableView 拖放到 UITableView 的教程

如何从 AVPlayer(不是 AVAudioPlayer)获取持续时间?

使用正则表达式在 Xcode 中查找/替换

如何编写 OS X Finder 插件

更改 UISearchBar 的字体大小

iOS 设备作为 Web 服务器

Objective-C 异常

使用 respondsToSelector 时 suppress '...' is deprecated

如何为 iPhone 6/7 自定义边到边图像指定尺寸?

Xcode 如何加载主情节提要?

如何使用图像和标签制作自定义 UIBarButtonItem?