抛出另一个答案,try 解释为什么performBlockAndWait
总是在调用线程中运行.
performBlock
是完全异步的.它将始终将块排队到接收MOC的队列中,然后立即返回.因此
[moc performBlock:^{
// Foo
}];
[moc performBlock:^{
// Bar
}];
将在主运行中心的队列中放置两个街区.它们将始终异步执行.一些未知线程将从队列中拉出块并执行它们.此外,这些块被包装在它们自己的自动释放池中,它们还将代表一个完整的核心数据用户事件(processPendingChanges
).
performBlockAndWait
不使用内部队列.它是在调用线程的上下文中执行的同步操作.当然,它将等待队列上的当前操作执行完毕,然后该块将在调用线程中执行.这一点已被记录(并在几次WWDC演示中得到重申).
此外,performBockAndWait
是可重入的,所以嵌套调用都会在调用线程中正确发生.
核心数据工程师已经非常清楚,运行基于队列的MOC操作的实际线程并不重要.关键在于使用performBlock*
API进行同步.
因此,考虑"PrimFultBug"为"这个块被放置在队列中,在某个未确定的时间内执行,在某个未确定的线程中.
performBlockAndWait
是"此块将在某个不确定的时间在这个完全相同的线程中执行.该函数将在该代码完全执行后返回(这将在与此MOC关联的当前队列耗尽后发生)."
EDIT
您确定"performBlockAndWait不使用内部队列"吗?
不幸的是,我用了同样的措辞来回答这个问题,因为就其本身而言,它是不正确的.然而,在原问题的背景下,它是正确的.具体来说,当在私有队列上调用performBlockAndWait
时,块将在调用函数的线程上执行——它不会被放入队列,而是在"私有线程"上执行
现在,在我深入讨论细节之前,我想强调,依赖库的内部工作是非常危险的.您真正应该关心的是,除了与主线程相关的任何内容外,您永远不能期望特定线程执行块.因此,不建议在主线程上执行performBlockAndWait
到not次,因为它将在调用它的线程上执行.
performBlockAndWait
使用GCD,但它也有自己的层(例如,防止死锁).如果查看GCD代码(这是开源代码),可以看到同步调用是如何工作的——通常它们与队列同步,并在调用函数的线程上调用块——除非队列是主队列或全局队列.此外,在WWDC会谈中,核心数据工程师强调performBlockAndWait
将在调用线程中运行.
所以,当我说它不使用内部队列时,这并不意味着它根本不使用数据 struct .它必须将调用与队列中已经存在的块以及在其他线程和其他异步调用中提交的块同步.然而,当拨打performBlockAndWait
时,它不会将阻塞置于队列中...相反,它会同步访问,并在调用函数的线程上运行提交的块.
现在,SO并不是一个很好的论坛,因为它比它复杂一点,特别是w.r.t主队列和GCD全局队列——但后者对于核心数据并不重要.
主要的一点是,当您调用任何performBlock*
或GCD函数时,您不应该期望它在任何特定线程上运行(绑定到主线程的东西除外),因为队列不是线程,只有主队列会在特定线程上运行块.
调用核心数据performBlockAndWait
时,块将在调用线程中执行(但将与提交到队列的所有内容适当同步).
我希望这是有道理的,尽管它可能只是造成了更多的混乱.
EDIT
此外,您可以看到这一点的潜在影响,即performBlockAndWait
提供可重入支持的方式打破了块的FIFO顺序.举个例子...
[context performBlockAndWait:^{
NSLog(@"One");
[context performBlock:^{
NSLog(@"Two");
}];
[context performBlockAndWait:^{
NSLog(@"Three");
}];
}];
请注意,严格遵守队列的FIFO保证将意味着嵌套performBlockAndWait
("三")将在异步块("两")之后运行,因为它是在异步块提交之后提交的.然而,事实并非如此,因为这是不可能的...出于同样的原因,嵌套的dispatch_sync
个调用会导致死锁.如果使用同步版本,需要注意的是.
一般来说,尽可能避免同步版本,因为dispatch_sync
会导致死锁,任何可重入版本,比如performBlockAndWait
,都必须做出一些"糟糕"的决定来支持它...比如让同步版本"跳转"队列.