我正在学习iOS并发编程。到目前为止,我已经阅读了关于NSOperation
/NSOperationQueue
和GCD
的内容。使用NSOperationQueue
和GCD
的原因是什么?它们之间的关系不太清楚,希望能得到一些反馈!
听起来GCD
和NSOperationQueue
都可以将NSThreads
的创建从用户中抽象出来。然而,两种方法之间的关系对我来说并不清晰,因此非常感谢任何反馈!
我正在学习iOS并发编程。到目前为止,我已经阅读了关于NSOperation
/NSOperationQueue
和GCD
的内容。使用NSOperationQueue
和GCD
的原因是什么?它们之间的关系不太清楚,希望能得到一些反馈!
听起来GCD
和NSOperationQueue
都可以将NSThreads
的创建从用户中抽象出来。然而,两种方法之间的关系对我来说并不清晰,因此非常感谢任何反馈!
GCD
是一个基于 C 语言的低级 API,可以非常简单地使用任务并发模型。 NSOperation
和 NSOperationQueue
是 Objective-C 类,它们也做了类似的事情。 NSOperation
首先被介绍,但是从10.5和iOS 2开始,NSOperationQueue
及其相关类已经内部实现了GCD
。
通常情况下,您应该使用最适合您需求的最高级别的抽象。这意味着通常应该使用NSOperationQueue
而不是GCD
,除非您需要做一些NSOperationQueue
不支持的事情。
请注意,NSOperationQueue
不是 GCD 的“简化”版;事实上,有很多东西您可以用NSOperationQueue
非常简单地完成,但使用纯粹的GCD
会很困难。(例如带宽受限队列,每次只运行 N 次操作; 在操作之间建立依赖关系。这两者都对NSOperation
来说非常简单,但对于GCD
则很困难。)苹果已经利用了 GCD 做出了很多努力,创建了一个非常好的面向对象的 API,即NSOperation
,请利用他们的工作,除非您有理由不这样做。
警告:
另一方面,如果您只需要发送一个块,并且不需要NSOperationQueue
提供的任何其他功能,则使用 GCD 没有任何问题。只要确保它是适合此任务的正确工具。
根据我对一个相关问题的回答,我要不同意BJ的意见,并建议您首先查看GCD而不是NSOperation/NSOperationQueue,除非后者提供了GCD无法满足的功能。
GCD出现之前,我在应用程序中使用了大量的NSOperations / NSOperationQueues来管理并发。然而,自从我开始经常使用GCD后,我几乎完全用块和分派队列替换了NSOperations和NSOperationQueues。这是因为我在实践中使用这两种技术的方式以及我对它们进行的剖析。
首先,当使用NSOperations和NSOperationQueues时,存在相当多的开销。这些都是Cocoa对象,需要进行分配和解除分配。我编写的一个iOS应用程序以60 FPS渲染3D场景时,我使用NSOperations封装每个渲染帧。当我对此进行剖析时,创建和拆除这些NSOperations占用了正在运行的应用程序的相当一部分CPU周期,并且会拖慢速度。我用简单的块和GCD串行队列替换了它们,这种开销消失了,导致渲染性能明显提高。这不是我注意到使用NSOperations时开销的唯一地方,在Mac和iOS上都看到过这种情况。
其次,当使用NSOperations时,难以与块式分派代码的优雅相匹配。将几行代码包装在块中并将其分派到串行或并发队列上,非常方便,而创建自定义NSOperation或NSInvocationOperation来执行此操作需要更多的支持代码。我知道您可以使用NSBlockOperation,但还不如直接将其分派到GCD。将此代码与应用程序中相关处理内联包装,可以使代码组织更好,而不是使用单独的方法或自定义NSOperations封装这些任务。
NSOperations和NSOperationQueues仍然具有非常好的用途。GCD没有真正的依赖关系概念,而NSOperationQueues可以设置相当复杂的依赖图。我在一些情况下使用NSOperationQueues。
总的来说,虽然我通常主张使用最高级别的抽象来完成任务,但这是一个例外情况,我主张使用更低级别的GCD API。在我与iOS和Mac开发者谈论此事时,绝大多数人选择使用GCD而不是NSOperations,除非他们的目标操作系统版本不支持它(早于iOS 4.0和Snow Leopard)。
GCD
是一个基于C语言的低级API。
NSOperation
和NSOperationQueue
是Objective-C类。
NSOperationQueue
是对GCD
的Objective-C封装。
如果你正在使用NSOperation
,那么你就隐式地使用了Grand Central Dispatch。
GCD相较于NSOperation的优势:
i. 实现
对于GCD
,其实现非常轻量级
NSOperationQueue
则是复杂而且重量级的
NSOperation相较于GCD的优势:
i. 对操作的控制
你可以暂停、取消、恢复一个NSOperation
ii. 依赖关系
你可以在两个NSOperations
之间建立依赖关系
只有当它的所有依赖项状态为已完成时,该操作才会开始。
iii. 操作或操作队列的状态
可以监视操作或操作队列的状态。
准备好,执行或完成
iv. 最大操作数
你可以指定同时运行的最大排队操作数
何时选择GCD
或NSOperation
当你希望更多地控制队列(如上所述)时,请使用NSOperation
而对于简单的情况,如果你只想在后台做一些工作并且额外工作非常少,请使用GCD
参考:
https://cocoacasts.com/choosing-between-nsoperation-and-grand-central-dispatch/
http://iosinfopot.blogspot.in/2015/08/nsthread-vs-gcd-vs-nsoperationqueue.html
选择 NSOperation 而不是 GCD 的另一个原因是 NSOperation 具有取消机制。例如,像 500px 这样显示数十张照片的应用程序,使用 NSOperation 可以在滚动表格视图或集合视图时取消不可见图像单元的请求,这可以大大提高应用程序性能并减少内存占用。GCD 无法轻松支持此功能。
此外,使用 NSOperation,KVO 是可能的。
这里有一篇来自Eschaton值得一读的文章。
NSOperation
,因为 NSURLSessionTask.cancel
和 NSURLSession.invalidateAndCancel
提供了这个功能。通常情况下,NSURLSession
提供了一些 NSOperationQueue
的功能,因为 NSURLSessionTask
提供了一些 NSOperation
的功能。 - algalGCD的级别确实比NSOperationQueue低,它的主要优点是其实现非常轻量级,并且专注于无锁算法和性能。
NSOperationQueue提供了一些GCD不可用的功能,但这些功能的代价是相当高的。NSOperationQueue的实现复杂而重量级,涉及大量的锁定,并且仅以非常最小的方式在内部使用GCD。
如果你需要NSOperationQueue提供的功能,请务必使用它。但是,如果GCD对你的需求足够,我建议直接使用它,以获得更好的性能、显著降低CPU和功率成本以及更多的灵活性。
NSQueueOperations和GCD都允许在后台的独立线程上执行重型计算任务,从而释放UI应用程序主线程。
根据先前的帖子,我们看到NSOperations具有addDependency,因此您可以按顺序将操作排队一个接一个地执行。
但我也了解到,您可以使用GCD串行队列创建其运行操作队列,使用dispatch_queue_create。这将允许以顺序方式依次运行一组操作。
NSQueueOperation优势超过GCD:
它允许添加依赖项并允许您删除依赖项,因此您可以使用依赖关系运行一个事务并发运行另一个事务,而GCD不允许以这种方式运行。
如果处于队列中,可以轻松取消操作,并且如果正在运行,则可以停止它。
您可以定义最大并发操作数量。
您可以暂停在队列中的操作
您可以查找队列中有多少待处理操作。
GCD非常易于使用——如果您希望在后台执行某项任务,只需编写代码并将其分派到后台队列即可。使用NSOperation完成相同的任务需要大量额外的工作。
NSOperation的优点是(a)您拥有一个真正的对象可以向其发送消息,以及(b)您可以取消一个NSOperation。这不是微不足道的。您需要子类化NSOperation,正确编写代码以使取消并正确完成任务都能正确工作。因此,对于简单的任务,您可以使用GCD,而对于更复杂的任务,则需要创建NSOperation的子类。(有NSInvocationOperation和NSBlockOperation等子类,但它们所做的一切都可以使用GCD更轻松地实现,因此没有充分的理由使用它们)。
我同意@Sangram和其他答案,但想补充几点。如果我错了,请纠正我。
我认为现在@Sangram的回答中的前两个点不再有效 (i.操作控制ii.依赖关系)。我们也可以使用GCD来实现这两点。试图通过代码解释(不要关注代码质量,仅供参考)
func methodsOfGCD() {
let concurrentQueue = DispatchQueue.init(label: "MyQueue", qos: .background, attributes: .concurrent)
//We can suspend and resume Like this
concurrentQueue.suspend()
concurrentQueue.resume()
//We can cancel using DispatchWorkItem
let workItem = DispatchWorkItem {
print("Do something")
}
concurrentQueue.async(execute: workItem)
workItem.cancel()
//Cam add dependency like this.
//Operation 1
concurrentQueue.async(flags: .barrier) {
print("Operation1")
}
//Operation 2
concurrentQueue.async(flags: .barrier) {
print("Operation2")
}
//Operation 3.
//Operation 3 have dependency on Operation1 and Operation2. Once 1 and 2 will finish will execute Operation 3. Here operation queue work as a serial queue.
concurrentQueue.async(flags: .barrier) {
print("Operation3")
}
}
let workItem = DispatchWorkItem
。 - Ol Sen