核心数据:父上下文阻止孩子

我正在做一些有核心数据的应用程序的后台处理。 后台处理是在一个子managedObjectContext上完成的。 上下文初始化

appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate // the moc in appDelegate is created with .MainQueueConcurrencyType mainThreadMOC = appDelegate.managedObjectContext! backgroundMOC = NSManagedObjectContext(concurrencyType:NSManagedObjectContextConcurrencyType.PrivateQueueConcurrencyType) backgroundMOC?.parentContext = mainThreadMOC 

后台处理按以下方法完成:

 // download all new transaction log entries func syncItems() { ... set up the query object for parse let moc = CoreDataStore.sharedInstance.backgroundMOC // perform download moc?.performBlock( { self.runQuery(query) // Download stuff und do some core data work }) } 

debugging器显示块内的所有工作确实在后台线程中。

当我从主线程调用这个函数并立即用一个冗长的核心数据操作阻塞主线程(用于testing目的)时,我看到后台线程停止并且只在主线程空闲时继续执行。

 // this is called from a view controller in the main thread syncItems() // should start to work in background for i in 0...200 { // do some core data work in main thread } // syncItems starts to work after the blocking for-loop ends. 

为什么会这样呢?

不要使用父子上下文设置。

亲子关系对于任何事情都不是一个好的方法。 只需使用一个简单的堆栈:两个上下文与一个共享持久性存储协调器。

亲子上下文只是添加了很多混淆,没有给你任何东西。 这是一个相当误解的概念。 我希望像mzarra这样的人会停止提倡这种设置。 这对社区是一种伤害。

如果您的后台上下文是主上下文的子上下文,则无论何时需要保存后台上下文,您都必须locking主上下文和后台上下文。 这会阻止用户界面。 而且您必须第二次locking用户界面以将这些更改从用户界面传播到PSC。 如果使用后台上下文,则必须将更改合并到主上下文中,但只能为当前在该上下文中注册的那些对象执行操作。 如果添加了新的对象,或者更新/删除了当前未被注册(引用)的对象,这些对象基本上是空的。

当你在评论“在主线程中做一些核心数据工作”时说的是访问mainThreadMOC工作吗?

这听起来像也许主线程的工作是locking运行runQuery需要访问的东西。

尝试将阻塞主线程的testing繁忙工作更改为不访问核心数据( NSThread.sleepForTimeInterval应该这样做)的东西,并查看是否允许后台runQuery工作。

如果这是问题,则需要将主线程工作重构为不阻止runQuery发生的任何事情。

我怀疑你的问题是双重的,即使你正在为testing目的而做,你的

冗长的核心数据操作

在主线程上阻塞UI,并通过你的定义你的backgroundMOC?.parentContext = mainThreadMOC

我build议为你的多个NSManagedObjectContext创build一个更健壮的结构。

我build议你按照这个答案中列出的步骤。

另外,你可以创build一个额外的MOC来pipe理你的runQuery

 discreteMOC = NSManagedObjectContext(concurrencyType:NSManagedObjectContextConcurrencyType.PrivateQueueConcurrencyType) discreteMOC?.parentContext = backgroundMOC //MOCprivate in my previous answer. 

希望这可以帮助。

关于家长的情况,不幸的是,我最后一次检查文件是相当简单的,在线上,你会发现几乎每一个人都倒过来。 核心数据在2011年还有一场WWDC会议,一切都清楚了,如果仔细看看API,就会开始有意义。

孩子不是背景 – 这是主要背景。 孩子是你通常与之交往的背景。 父母是背景上下文。

孩子将更改推送给其父母。 这就是为什么父母有一个persistentStoreCoordinator,但孩子而不是有一个parentContext(它不需要一个persistentStoreCoordinator)。

因此,孩子是主要(UI)队列中的主要上下文。 它将更改保存到其父项中。 这发生在内存中(快速 – 尽可能快地离开UI)。

父上下文然后通过其persistentStoreCoordinator将其更改保存到持久存储中。 这就是为什么父母在一个私人队列。

 lazy var managedObjectContext: NSManagedObjectContext = { let parentContext = NSManagedObjectContext(concurrencyType: .PrivateQueueConcurrencyType) parentContext.persistentStoreCoordinator = self.persistentStoreCoordinator let managedObjectContext = NSManagedObjectContext(concurrencyType: .MainQueueConcurrencyType) managedObjectContext.parentContext = parentContext return managedObjectContext }() 

还有其他的优化,比如将.undoManager设置为零,但是这个通用的体系结构对于后台保存来说是完美无缺的。

您可能还想要一个保存方法,接收完成块/closures,立即保存在您的子队列(只保存到父,如上所述),然后调用parentContext的performBlock方法,你将它保存(在它的私有队列中)join到底层持久化存储中(相对较慢,但现在是非阻塞的),然后调用你的完成块/closures(你已经使用GCD设置在主队列上运行,否则你打电话从父节点的performBlock方法返回到主队列。

当你不反转架构的时候,一切都很顺利。 我不确定它是如何开始在线的,但这几乎是一个普遍的错误。

祝你好运。