2017 / Swift 3.1 - GCD 与 NSOperation

2017 / Swift 3.1 - GCD vs NSOperation

我正在更深入地研究并发性,并且广泛阅读了有关 GCD 和 NSOperation 的内容。然而,很多像 canonic answer on SO 这样的帖子已经有好几年了。

在我看来,NSOperation 过去的主要优点是,以牺牲一些性能为代价:

特别是考虑到 GCD 的 DispatchWorkItem 和区块取消 / DispatchGroup / qos,是否真的有动力(性价比方面)再使用 NSOperation除了需要能够在任务开始执行或查询任务状态时取消任务的情况之外的并发性?

Apple 似乎更加重视 GCD,至少在他们的 WWDC 中是这样(假设它比 NSOperation 更新)。

我看到他们每个人都有自己的目的。我最近刚刚重新观看了 2015 WWDC 的讨论(Advanced NSOperations),我在这里看到了两个要点。

运行 时间和用户互动

来自谈话:

NSOperations run for a little bit longer than you would expect a block to run, so blocks usually take a few nanoseconds, maybe at most a millisecond, to execute.

NSOperations, on the other hand, can be much longer, for anywhere from a couple of milliseconds to even several minutes

他们谈论的例子是在 WWDC 应用程序中,其中存在一个依赖于登录用户的 NSOperation。依赖项 NSOperation 提供了一个登录视图控制器并等待用户进行身份验证。完成后,NSOperation 完成,NSOperationQueue 恢复其工作。我认为您不想在这种情况下使用 GCD。

Subclassing

由于 NSOperations 只是 classes,您可以子class 它们以获得更多的可重用性。这对于 GCD 是不可能的。

示例:(使用上面的 WWDC 登录场景)

您的代码库中有许多 NSOperations 与需要对它们进行身份验证的用户交互相关联。 (在这个例子中,点赞一个视频。)您可以扩展 NSOperation 来创建一个 AuthenticatedOperation,然后让所有这些 NSOperations 扩展这个新的 class。

首先,NSOperationQueue 让您将 操作 入队,也就是说,某种使用 start 方法的异步操作, cancel 方法和一些可观察的属性,而使用调度队列可以提交 blockclosurefunction 到调度队列,然后执行。

"Operation" 在语义上与块(或闭包、函数)有根本的不同。一个操作有一个底层的异步任务,而一个块(闭包或函数)就是这样。

接近NSOperation的是异步函数,例如:

func asyncTask(param: Param, completion: (T?, Error?) ->())

现在使用 Futures 我们可以定义相同的异步函数,如:

func asyncTask(param: Param) -> Future<T>

这使得这种异步函数非常方便。

由于 futures 具有组合函数,如 mapflatMap 等,我们可以很容易地 "emulate" NSOperation 的 "dependency" 功能,只需以更强大、更简洁、更易于理解的方式。

我们也可以用几行完全基于 GCD 的代码来实现某种 NSOperationQueue,例如 "TaskQueue" 并且具有基本相同的功能,例如 "maxConcurrentTasks" 并且可以用它来排队任务函数(不是操作),只是以一种更强大、更简洁和更易于理解的方式。 ;)

为了获得可取消的操作,您需要创建 NSOperation 的子类 - 同时您可以创建异步函数 "ad-hod" - 内联。

此外,由于 取消 是一个独立的概念,我们可以假设,存在一些库,其实现完全基于 GCD,它解决了这个问题,嗯,通常的方式 ;) 它可能看起来像这样:

self.cancellationRequest = CancellationRequest()
self.asyncTask(param: param, cancellationToken: cr.token).map { result in
   ...
}

及以后:

override func viewWillDisappear(_ animated: animated) {
    super.viewWillDisappear(animated)
    self.cancellationRequest.cancel() 
}

所以,恕我直言,真的没有理由使用笨重的 NSOperationNSOperationQueue,也没有理由再对 NSOperation 进行子类化,这是非常复杂和令人惊讶的困难,除非你不关心数据竞争。