抛出另一个答案,尝试解释为什么performBlockAndWait 将始终在调用线程中运行。
performBlock 是完全异步的。它总是将块排入接收 MOC 的队列,然后立即返回。因此,
[moc performBlock:^{
// Foo
}];
[moc performBlock:^{
// Bar
}];
会在 moc 队列中放置两个块。它们将始终异步执行。一些未知线程将从队列中拉出块并执行它们。此外,这些块被包装在它们自己的自动释放池中,它们也将代表一个完整的 Core Data 用户事件 (processPendingChanges)。
performBlockAndWait 不使用内部队列。它是在调用线程的上下文中执行的同步操作。当然,它会等到队列上的当前操作已经执行完毕,然后该块将在调用线程中执行。这已记录在案(并在多个 WWDC 演示文稿中重申)。
此外,performBockAndWait 是可重入的,因此嵌套调用都发生在该调用线程中。
Core Data 工程师非常清楚,运行基于队列的 MOC 操作的实际线程并不重要。关键是使用performBlock* API 进行同步。
因此,将 'performBlock' 视为“此块被放置在队列中,将在某个未确定的时间,在某个未确定的线程中执行。该函数将在入队后立即返回给调用者”
performBlockAndWait 是“这个块将在某个未确定的时间,在这个完全相同的线程中执行。该函数将在此代码完全执行后返回(这将在与此 MOC 关联的当前队列耗尽后发生)。 "
编辑
您确定“performBlockAndWait 不使用内部队列”吗?
我认为确实如此。唯一的区别是 performBlockAndWait 将
等到块完成。你打电话是什么意思
线?据我了解,[moc performBlockAndWait] 和 [moc
performBloc] 都在其私有队列(后台或主)上运行。这
这里的重要概念是 moc 拥有队列,而不是相反
大约。如果我错了,请纠正我。 – Philip007
不幸的是,我按照自己的方式表达了答案,因为就其本身而言,它是不正确的。但是,在原始问题的上下文中,它是正确的。具体来说,当在私有队列上调用performBlockAndWait 时,该块将在调用该函数的线程上执行——它不会被放入队列并在“私有线程”上执行。
现在,在我深入细节之前,我想强调一下,依赖库的内部运作是非常危险的。您真正应该关心的是,您永远不能期望特定线程执行块,除非与主线程相关联。因此,不建议期望 performBlockAndWait 在主线程上不执行,因为它将在调用它的线程上执行。
performBlockAndWait 使用 GCD,但它也有自己的层(例如,防止死锁)。如果您查看 GCD 代码(它是开源的),您可以看到同步调用是如何工作的 - 通常它们与队列同步并调用调用函数的线程上的块 - 除非队列是主队列或一个全局队列。此外,在 WWDC 演讲中,Core Data 工程师强调performBlockAndWait 将在调用线程中运行。
所以,当我说它不使用内部队列时,并不意味着它根本不使用数据结构。它必须将调用与队列中已经存在的块以及在其他线程和其他异步调用中提交的块同步。但是,当调用 performBlockAndWait 时,它不会将块放入队列中……而是同步访问并在调用该函数的线程上运行提交的块。
现在,SO 不是一个很好的论坛,因为它比这更复杂一些,尤其是主队列和 GCD 全局队列 - 但后者对 Core Data 并不重要。
主要的一点是,当你调用任何performBlock* 或 GCD 函数时,你不应该期望它在任何特定线程上运行(除了与主线程相关的东西),因为队列不是线程,而只是主队列将在特定线程上运行块。
当调用核心数据performBlockAndWait 时,块将在调用线程中执行(但会与提交到队列的所有内容适当同步)。
我希望这是有道理的,尽管它可能只会引起更多的混乱。
编辑
此外,您可以看到这不言而喻的含义,因为performBlockAndWait 提供重入支持的方式打破了块的 FIFO 排序。举个例子……
[context performBlockAndWait:^{
NSLog(@"One");
[context performBlock:^{
NSLog(@"Two");
}];
[context performBlockAndWait:^{
NSLog(@"Three");
}];
}];
请注意,严格遵守队列的 FIFO 保证意味着嵌套的performBlockAndWait(“三”)将在异步块(“二”)之后运行,因为它是在提交异步块之后提交的。然而,这不是发生的事情,因为这是不可能的......出于同样的原因,嵌套的dispatch_sync 调用会导致死锁。如果使用同步版本,请注意一些事项。
一般来说,尽可能避免同步版本,因为dispatch_sync 会导致死锁,并且任何可重入版本,如performBlockAndWait 都必须做出一些“糟糕”的决定来支持它......比如拥有同步版本“跳”队列。