为什么在高级应用程序中我应该选择GCD而不是NSOperation和blocks?

34

苹果的Grand Central Dispatch参考文档中这样说:

 

“……如果您的应用程序需要在系统的Unix级别上运行,例如,如果它需要操作文件描述符、Mach端口、信号或计时器。 GCD不仅限于系统级应用程序,但在将其用于更高级别的应用程序之前,您应该考虑通过Cocoa(通过NSOperation和block对象)提供的类似功能是否更容易使用或更适合您的需求。”。

http://developer.apple.com/library/ios/#documentation/Performance/Reference/GCD_libdispatch_Ref/Reference/reference.html

我实际上无法想象,在高级应用程序中,必须使用GCD而不能使用NSOperation的情况。

有什么想法吗?


https://cocoacasts.com/choosing-between-nsoperation-and-grand-central-dispatch/ - Masih
5个回答

59
这里所说的要点与Chris Hanson在他的文章“何时使用NSOperation vs. GCD”中所述的一致:
直接的答案是所有应用程序开发的通用指导原则: 始终使用可用的最高级抽象,当测量表明需要时,降到较低级别的抽象。
在这个特定的情况下,这意味着在编写Cocoa应用程序时,您通常应该使用NSOperation而不是直接使用GCD。这并不是因为效率上的差异,而是因为NSOperation在GCD机制之上提供了更高级别的抽象。
总的来说,我同意这种观点。NSOperation和NSOperationQueue支持依赖关系和其他一两个东西,而GCD块和队列没有,它们抽象掉了并发操作的低级细节。如果您需要这些功能,则NSOperation是一个非常好的选择。
然而,在使用两者后,我发现自己用GCD块和队列替换了所有基于NSOperation的代码。我这样做有两个原因:使用NSOperation进行频繁操作时存在显著的开销,而且我认为使用GCD块时我的代码更加清晰和描述性。
第一个原因来自我的应用程序中的分析,我发现在处理小而频繁的操作(如将OpenGL ES帧呈现到屏幕上)时,NSOperation对象分配和释放过程需要大量的CPU资源。GCD块完全消除了这种开销,从而带来了显着的性能提升。
第二个原因更加主观,但是我认为使用块比NSOperations时我的代码更加简洁。块所提供的快速作用域捕获和内联特性使得代码更少,因为你不需要创建自定义的NSOperation子类或者打包要传递到操作中的参数,并且在我看来,代码更加描述性,因为你可以在触发的地方将要运行的代码放置在队列中。
再次强调,这是个人偏好,但是我发现即使在抽象程度更高的Cocoa应用程序中,我也更多地使用GCD。

NSOperationQueue现在可以使用闭包了。这个答案需要(稍微)更新吗?另外,过去5年中是否有优化NSOperation以减少您发现的开销的机会? - Hayden Holligan
我应该补充说明,我最终做了完全相反的事情 - 在多年使用GCD之后,转换到NSOperationQueue是一股清新的空气。我的“排队”行为现在已经定义得很好,我可以完全控制最大并发操作。取消正在运行的操作并用新操作替换它们也更容易,从而产生了良好的定义行为。我仍然使用GCD进行简单的异步操作,但我已经转换到NSOperationQueue以满足我的串行/异步排队需求。 - strangetimes
3
从2020年的Swift角度来看,OperationOperationQueue是类型NSObject(Objective-C遗产),而DispatchQueueDispatchWorkItemDispatchGroup则不是。 - l --marc l

15
  • 如果任务不是太复杂且需要最佳的 CPU 性能,则建议使用 GCD。
  • 如果任务较为复杂,需要取消或暂停某个块以及进行依赖管理,则建议使用 NSOperationQueue。

GCD 是一种轻量级的方式,用于表示将要并发执行的工作单元。您无需安排这些工作单元;系统会为您处理调度。在块之间添加依赖关系可能会让人头痛。取消或暂停块会给开发者带来额外的工作!

与 GCD 相比,NSOperation 和 NSOperationQueue 会增加一些额外的开销,但您可以在各种操作之间添加依赖关系。您可以重复使用、取消或暂停操作。NSOperation 兼容键值观察(KVO);例如,您可以通过监听 NSNotificationCenter 来启动 NSOperation。

更多详细的解释,请参考此问题:NSOperation vs Grand Central Dispatch


4

NSOperation没有dispatch_source_t、dispatch_io、dispatch_data_t、dispatch_semaphore_t等等的相应功能,而且它的开销有点大。

另一方面,libdispatch没有操作依赖性、操作优先级(队列优先级有些不同)或操作的KVO等等功能。


1
NSOperation 现在是基于 GCD 构建的,因此除非其中一个有你所需而另一个没有的东西,否则它们之间并没有太大的优势。 - hypercrypt
1
它仍然需要为操作进行对象分配,以及其他复杂性。对于非常细粒度的线程处理来说,这实际上可能是一个有意义的开销。是的,我已经测量过了;是的,对于我的一些代码来说这是至关重要的。NSOperation对GCD的封装并不像人们期望的那样简单,因为它需要支持依赖关系、KVO和优先级。 - Catfish_Man
他是正确的,我也发现在频繁发生的操作中使用NSOperation会有很大的开销,而使用GCD块则不会出现这种情况。在这些情况下,对象的分配/释放可能非常昂贵,因此GCD可能更合适。 - Brad Larson

1
NSOperationQueue可以做两件GCD不能做的事情:一是依赖关系(将操作添加到队列中,但告诉它只有在某些其他操作完成后才执行),二是NSOperation在任务执行时可以接收消息的对象,而GCD只有块,除了非常有限的方式外无法接收消息。你需要这两个功能之一或不需要。如果你不需要,使用GCD会更容易使用。
这就是为什么有用的NSOperation示例总是相当复杂的原因。如果它们很容易,那么你会使用GCD。通常,您要创建NSOperation的子类,这将是相当大量的工作,或使用其他人创建的子类。

0

我最近在阅读相关资料,不出所料,人们对此有不同的看法。

我想不到什么情况下需要使用GCD而不是NSOperation,但这并不意味着不存在这样的情况。然而,就最佳实践编码而言,我同意一般的观点:

如果你有几个工具都能胜任工作(在这种情况下,你可以选择NSOperation或者GCD block),那么就使用抽象级别最高的类(即最高级别的API)。这不仅通常更容易使用/代码更少,而且还可以从未来可能引入的更高级别API的潜在增强中获益。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接