WWDC 2015 Session 226 Advanced NSOperation

本文最后更新于 2021年4月4日 晚上

今天看了 WWDC 2015 Session 226 有关 NSOperationNSOperationQueue 的内容(Advanced NSOperations), 故继续记录下来.

Core Concepts(核心概念)

NSOperationDispatchBlock 的更高一级抽象, 提供更加易用的接口, 而 NSOperationQueue 可以理解为高级的 DispatchQueue.

任务的并行和串行执行

假设有如下的代码:

1
2
let opq = OperationQueue()
opq.maxConcurrentOperationCount = 1

则这个 OperationQueue 的所有任务就是串行执行的.

如果将并行操作的数量指定为如下, 则程序会自动设置为最大可以并行执行的数量:

1
2
opq.maxConcurrentOperationCount = OperationQueue.defaultMaxConcurrentOperationCount

NSOperation 的特点

Operation 的执行时间会比单独的 block 执行时间更长. 另外 Operation 是一个抽象类, 可以继承它来实现一些自己想要的功能.

Swift 中的 Operation 类是抽象类, 实际使用时用它的系统内置子类比如 BlockOperation 等或你自己的自定义子类.

Operation 的生命期:

  1. 当初始化后, Operation 处于 pending 状态, 这时它即将被添加到队列, 且未开始执行.

  2. 当准备执行时, 它是 ready 状态

  3. 当执行时, 变为 executing 状态

  4. 执行结束后, 变为最终状态 finished

  5. 另外在到达 finished 状态前, 它随时都可以变为 cancelled 状态.

Operation 一共可以有 5 种状态, 下面来看看一些典型的转换场景.

  1. 除了 finished 状态, 另外 3 种状态下都可以转换为 cancelled, 只要执行如下代码:
1
2
3
let op = ...
// ...
op.cancel()
  1. 在串行队列的情况下(maxConcurrentOperationCount 等于 1), 任务的执行顺序可能不是其添加到队列的顺序, 因为 OperationQueue 会找到队列中当前最前面的一个处于 ready 状态的 Operation 来执行(如果前面 3 个都是 pending, 而第四个是 ready, 则会先调度执行第四个).

这样可能导致任务执行顺序混乱, 解决方法是利用任务间的依赖关系来指定严格的执行顺序, 并且这样的依赖关系并不仅限在单一的队列中(即一个队列中的任务可以依赖另外一个队列中的任务执行成功后再执行).

1
2
3
let opA = ...
let opB = ...
opA.addDependency(opB)

在设置任务间依赖时要注意避免产生死锁, 如果 A 依赖 B, B 依赖 A, 则二者均在等待对方执行完成, 这时就会产生死锁.

在实际开发过程中, 可以利用依赖实现很多常见开发任务, 比如登录的时候首先需要获取公钥, 然后加密密钥, 然后进行登录, 登录成功后再跳转. 这时就可以定义任务 A 是获取公钥, 任务 B 是加密密码, 任务 C 是登录. 建立 A–>B–>C 这样的依赖关系, 就不用再用回调地狱来处理了.

任务的执行结果可以是成功或失败, 但只要是有了执行结果, 任务状态就是 finished.

在实际工作中如何选择使用 Operation 还是 GCD 呢? 实际上它们二者各自有各自适合的使用场景. 针对轻量级的场景, 则使用 GCD, 针对重型任务打包处理, 则使用 Operation.

Beyond the Basics(进阶)

BlockOperation

它是 Operation 的具体子类:

1
2
3
let blockOP = BlockOperation(block: {
// ...
})

BlockOperation 类对象可以设置一个或多个 block, 这些 block 可以一次性执行, 而无需将它们分别放到不同的 BlockOperation 中.

当所有的 block 执行结束后, BlockOperation 的状态才会变为 finished.

任务组合(Composing Operations)

上面讲的任务间的依赖关系是这样的: 如果 A 依赖 B, 则 B 执行结束后, A 才执行.

但有些情况是这样的: 如果 A 依赖 B, 且任务 A 只有当网络畅通的情况下才能被执行, 那该如何表达?

比如应用要下载图片数据, 并解析图片数据, 然后再对图片进行后面的处理. 这个时候可以将下载和解析两个任务封装到一个更大的任务中.

任务互斥

比如在同一时间, 只能有一个弹窗显示, 其他的弹窗只能等待或不显示, 此时也可以使用 OperationQueue 达到目的. 实际上也是使用任务的依赖, 即第二个依赖第一个任务执行结束.

Sample Code(示例代码)

代码可以在这个链接中下载.

比如任务组合就是使用 Group 实现.

使用 OperationObserver 可以进行很多有价值的操作, 比如当某个任务正在进行, 但程序进入后台, 则可以用它观察后启动一个任务来继续之前的操作.

自定义 Operation 类, 并且引入两个核心概念:

  • Condition: 任务相关的协议, 用于表达一个任务如何生成它的依赖, 如何定义互斥, 以及任务的执行条件.

  • Observer: 用于观察任务的状态并进行预定义或自定义的操作.

  • 各种额外的自定义 Operation 类.

实践中, 可以将业务逻辑封装到一个 Operaiton 中去执行. 利用任务间的依赖, 可以很容易实现复杂逻辑, 或是定义复杂行为.


WWDC 2015 Session 226 Advanced NSOperation
https://blog.rayy.top/2018/10/01/2019-8-wwdc2015session226-nsoperationqueue/
作者
貘鸣
发布于
2018年10月1日
许可协议