我正在专用队列中创建NSManagedObjectContext
来处理我从文件和/或服务中获取的数据更新:
NSManagedObjectContext *privateContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
AppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
privateContext.persistentStoreCoordinator = appDelegate.persistentStoreCoordinator;
由于我使用的是专用队列,因此我不完全了解
performBlock:
和performBlockAndWait:
方法之间的区别...为了执行数据更新,我目前正在这样做:[privateContext performBlock: ^{
// Parse files and/or call services and parse
// their responses
// Save context
[privateContext save:nil];
dispatch_async(dispatch_get_main_queue(), ^{
// Notify update to user
});
}];
在这种情况下,我的数据更新是同步同步的,所以我认为那是保存上下文的正确位置,对吗?如果我做错了,如果您让我知道,我将不胜感激。另一方面,此代码是否等效?:
[privateContext performBlockAndWait: ^{
// Parse files and/or call services and parse
// their responses
// Save context
[privateContext save:nil];
}];
// Notify update to user
再次,我猜这是保存上下文的正确位置……这两种方法之间有什么区别(在这种情况下,如果有的话)?
如果我需要执行异步服务调用而不是执行同步服务调用或文件解析怎么办?这些数据更新将如何管理?
提前致谢
最佳答案
您是对的,因为要对MOC进行的任何操作都必须在performBlock
或performBlockAndWait
内完成。请注意,对于托管对象而言,保留/释放是线程安全的,因此不必为了保留/释放托管对象上的引用计数而进入这些块之一。
它们都利用同步队列来处理消息,这意味着一次只能执行一个块。好吧,那几乎是对的。请参阅performBlockAndWait
的描述。无论如何,对MOC的访问将被序列化,使得一次仅一个线程正在访问MOC。
tl; dr不必担心差异,请始终使用performBlock
。
事实差异
有许多差异。我敢肯定还有更多,但是我认为最重要的是要理解这些。
同步与异步performBlock
是异步的,它立即返回,并且该块在将来的某个时间在某个未公开的线程上执行。通过performBlock
提供给MOC的所有块将按照它们添加的顺序执行。performBlockAndWait
是同步的,因为调用线程将等到该块执行后再返回。该块是在其他线程中运行还是在调用线程中运行并不那么重要,并且是无法信任的实现细节。
但是请注意,它可以实现为“嘿,其他线程,请运行此块。我要坐在这里什么也不做,直到您告诉我它已经完成。”或者,可以将其实现为“嘿,核心数据,给我一个锁,阻止其他所有块都运行,这样我就可以在自己的线程上运行此块了。”或者可以通过其他方式实现。同样,实现细节可能随时更改。
不过,我会告诉您,上一次我测试它时,performBlockAndWait
在调用线程上执行了该块(意味着上一段中的第二个选项)。这仅是真正的信息,可以帮助您了解正在发生的事情,不应以任何方式依赖它。
再入performBlock
始终是,是异步,因此不能重入。好吧,有些人可能认为它是可重入的,因为您可以在使用performBlock
调用的块中调用它。但是,如果执行此操作,则对performBlock
的所有调用将立即返回,并且直到至少当前执行块的完全完成其工作之后,该块才会执行。[moc performBlock:^{
doSomething();
[moc performBlock:^{
doSomethingElse();
}];
doSomeMore();
}];
这些功能将始终按以下顺序执行:doSomething()
doSomeMore()
doSomethingElse()
performBlockAndWait
始终为,是同步。此外,它也是可重入的。多个调用不会死锁。因此,如果最终由于另一个performBlockAndWait
而正在运行的块内时最终调用performBlockAndWait
,那就可以了。您将获得预期的行为,因为第二个调用(以及任何后续调用)不会导致死锁。此外,正如您所期望的,第二个将在返回之前完全执行。[moc performBlockAndWait:^{
doSomething();
[moc performBlockAndWait:^{
doSomethingElse();
}];
doSomeMore();
}];
这些功能将始终按以下顺序执行:doSomething()
doSomethingElse()
doSomeMore()
先进先出
FIFO代表“先进先出”,这意味着将按照将它们放入内部队列的顺序执行块。performBlock
始终采用内部队列的FIFO结构。每个块都将插入到队列中,并且只有在删除后才按FIFO顺序运行。
根据定义,performBlockAndWait
会中断FIFO排序,因为它会跳过已经排队的块队列。
使用performBlockAndWait
提交的块不必等待队列中正在运行的其他块。有很多方法可以看到这一点。一个简单的就是这个。[moc performBlock:^{
doSomething();
[moc performBlock:^{
doSomethingElse();
}];
doSomeMore();
[moc performBlockAndWait:^{
doSomethingAfterDoSomethingElse();
}];
doTheLastThing();
}];
这些功能将始终按以下顺序执行:doSomething()
doSomeMore()
doSomethingAfterDoSomethingElse()
doTheLastThing()
doSomethingElse()
在此示例中很明显,这就是我使用它的原因。但是,请考虑一下,如果您的MOC正在多个地方调用它。可能有点困惑。
但是要记住的一点是,performBlockAndWait
是抢占式的,可以跳过FIFO队列。
僵局
您将永远不会遇到调用performBlock
的僵局。如果您在块内执行愚蠢的操作,则可能会死锁,但是调用performBlock
绝不会死锁。您可以从任何地方调用它,它只会将块添加到队列中,并在将来的某个时间执行。
您很容易获得调用performBlockAndWait
的死锁,特别是如果您从外部实体可以在嵌套上下文中调用或不加选择地调用该方法的情况下,尤其如此。具体来说,如果 parent 在 child 身上调用performBlockAndWait
,几乎可以保证您的应用程序会死锁。
用户事件
Core Data认为“用户事件”是对processPendingChanges
的调用之间的任何事件。您可以阅读为什么使用此方法很重要的详细信息,但是在“用户事件”中发生的情况会影响通知,撤消管理,删除传播,更改合并等。performBlock
封装了一个“用户事件”,这意味着代码块在processPendingChanges
的不同调用之间自动执行。performBlockAndWait
不封装“用户事件”。如果要将块视为不同的用户事件,则必须自己执行。
自动释放池performBlock
将块包装在其自己的autoreleasepool中。performBlockAdWait
没有提供唯一的自动释放池。如果需要一个,则必须自己提供。
个人意见
我个人认为使用performBlockAndWait
没有很多很好的理由。我确信有人有用例无法以其他任何方式完成,但我还没有看到它。如果有人知道该用例,请与我分享。
最接近的是在父上下文上调用performBlockAndWait
(永远不要在NSMainConcurrencyType
MOC上执行此操作,因为它可能会锁定您的UI)。例如,如果要确保在当前块返回之前,数据库已完全保存到磁盘,其他块有机会运行。
因此,不久前,我决定将Core Data视为完全异步的API。结果,我拥有大量的核心数据代码,并且在测试之外我没有一个单独的对performBlockAndWait
的调用。
这样生活会好很多。我遇到的问题要比以前回想的要少得多,当时我以为“它必须有用,否则他们就不会提供”。
现在,我不再需要performBlockAndWait
了。结果,也许它已经改变了一些,而我只是想念它,因为它不再使我感兴趣……但是我对此表示怀疑。
关于ios - performBlock : and performBlockAndWait:?之间的行为差异,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/32198678/