Sno*_*man 42 iphone core-data objective-c ios
我有一个NSManagedObjectContext声明如下:
- (NSManagedObjectContext *) backgroundMOC {
if (backgroundMOC != nil) {
return backgroundMOC;
}
backgroundMOC = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
return backgroundMOC;
}
Run Code Online (Sandbox Code Playgroud)
请注意,它使用专用队列并发类型声明,因此其任务应在后台线程上运行.我有以下代码:
-(void)testThreading
{
/* ok */
[self.backgroundMOC performBlock:^{
assert(![NSThread isMainThread]);
}];
/* CRASH */
[self.backgroundMOC performBlockAndWait:^{
assert(![NSThread isMainThread]);
}];
}
Run Code Online (Sandbox Code Playgroud)
为什么调用performBlockAndWait在主线程而不是后台线程上执行任务?
Jod*_*ins 98
抛弃另一个答案,尝试解释为什么performBlockAndWait总是在调用线程中运行.
performBlock是完全异步的.它总是将块排入接收MOC的队列,然后立即返回.从而,
[moc performBlock:^{
// Foo
}];
[moc performBlock:^{
// Bar
}];
Run Code Online (Sandbox Code Playgroud)
将在队列中放置两个块用于moc.它们总是异步执行.一些未知的线程会从队列中拉出块并执行它们.此外,这些块包含在它们自己的自动释放池中,并且它们也代表完整的Core Data用户事件(processPendingChanges).
performBlockAndWait不使用内部队列.它是在调用线程的上下文中执行的同步操作.当然,它将一直等到队列上的当前操作被执行,然后该块将在调用线程中执行.这是记录的(并在几个WWDC演示文稿中重申).
此外,performBockAndWait是重入的,所以嵌套调用都发生在该调用线程中.
核心数据工程师已经非常清楚,基于队列的MOC操作运行的实际线程并不重要.这是通过使用performBlock*API的关键同步.
因此,将'performBlock'视为"此块被放置在队列中,在某个未确定的时间执行,在某个未确定的线程中执行.该函数将在它入队后立即返回给调用者"
performBlockAndWait 是"该块将在某个时间未定被执行,在此相同的线程.这个代码后已经完全执行(与此相关联的MOC当前队列之后会出现已耗尽)该函数将返回".
编辑
你确定"performBlockAndWait不使用内部队列"吗?我认为确实如此.唯一的区别是performBlockAndWait将等到块完成.你通过调用线程是什么意思?根据我的理解,[moc performBlockAndWait]和[moc performBloc]都在其私有队列(后台或主队列)上运行.这里的重要概念是moc拥有队列,而不是相反.如果我错了,请纠正我. - Philip007
不幸的是,我像我一样表达了答案,因为它本身就是不正确的.但是,在原始问题的背景下,这是正确的.具体来说,当调用performBlockAndWait私有队列时,该块将在调用该函数的线程上执行 - 它不会被放入队列并在"私有线程"上执行.
现在,在我进入细节之前,我想强调一下,取决于图书馆的内部运作是非常危险的.所有你真正关心的是你永远不会指望一个特定的线程来执行一个块,除了与主线程相关的任何东西.因此,期待performBlockAndWait以不主线程上执行不建议,因为它会调用它的线程上执行.
performBlockAndWait使用GCD,但它也有自己的层(例如,以防止死锁).如果你看一下GCD代码(它是开源的),你可以看到同步调用是如何工作的 - 通常它们与队列同步并在调用函数的线程上调用块 - 除非队列是主队列或者全局队列.此外,在WWDC会谈中,核心数据工程师强调performBlockAndWait将在调用线程中运行的点.
因此,当我说它不使用内部队列时,这并不意味着它根本不使用数据结构.它必须将调用与队列中已有的块以及在其他线程和其他异步调用中提交的块同步.但是,当调用performBlockAndWait它时不会将块放在队列上...而是同步访问并在调用该函数的线程上运行提交的块.
现在,SO不是一个很好的论坛,因为它比这更复杂,尤其是主队列和GCD全局队列 - 但后者对核心数据并不重要.
重点是当你调用任何一个performBlock*或GCD函数时,你不应该期望它在任何特定的线程上运行(除了绑定到主线程的东西),因为队列不是线程,只有主队列才会运行特定的块线.
当调用核心数据时performBlockAndWait,块将在调用线程中执行(但将与提交给队列的所有内容适当地同步).
我希望这是有道理的,尽管它可能只会引起更多混乱.
编辑
此外,您可以看到这一点的未说明的含义,因为performBlockAndWait提供可重入支持的方式会破坏块的FIFO排序.举个例子...
[context performBlockAndWait:^{
NSLog(@"One");
[context performBlock:^{
NSLog(@"Two");
}];
[context performBlockAndWait:^{
NSLog(@"Three");
}];
}];
Run Code Online (Sandbox Code Playgroud)
请注意,严格遵守队列的FIFO保证意味着嵌套performBlockAndWait("Three")将在异步块("Two")之后运行,因为它是在提交异步块之后提交的.然而,这不是发生的事情,因为它是不可能的......出于同样的原因,嵌套dispatch_sync调用会导致死锁.如果使用同步版本,请注意一些事项.
一般情况下,尽可能避免同步版本因为dispatch_sync可能导致死锁,而任何重入版本performBlockAndWait都会做出一些"坏"决定来支持它......就像让同步版本"跳转"队列一样.
| 归档时间: |
|
| 查看次数: |
18465 次 |
| 最近记录: |