WWDC 2015 Session 226 Advanced NSOperation
本文最后更新于 2021年4月4日 晚上
今天看了 WWDC 2015 Session 226 有关 NSOperation
和 NSOperationQueue
的内容(Advanced NSOperations), 故继续记录下来.
Core Concepts(核心概念)
NSOperation
是 DispatchBlock
的更高一级抽象, 提供更加易用的接口, 而 NSOperationQueue
可以理解为高级的 DispatchQueue
.
任务的并行和串行执行
假设有如下的代码:
1 |
|
则这个 OperationQueue
的所有任务就是串行执行的.
如果将并行操作的数量指定为如下, 则程序会自动设置为最大可以并行执行的数量:
1 |
|
NSOperation 的特点
Operation
的执行时间会比单独的 block 执行时间更长. 另外 Operation
是一个抽象类, 可以继承它来实现一些自己想要的功能.
Swift 中的
Operation
类是抽象类, 实际使用时用它的系统内置子类比如BlockOperation
等或你自己的自定义子类.
Operation
的生命期:
当初始化后,
Operation
处于pending
状态, 这时它即将被添加到队列, 且未开始执行.当准备执行时, 它是
ready
状态当执行时, 变为
executing
状态执行结束后, 变为最终状态
finished
另外在到达
finished
状态前, 它随时都可以变为cancelled
状态.
故 Operation
一共可以有 5 种状态, 下面来看看一些典型的转换场景.
- 除了
finished
状态, 另外 3 种状态下都可以转换为cancelled
, 只要执行如下代码:
1 |
|
- 在串行队列的情况下(
maxConcurrentOperationCount
等于 1), 任务的执行顺序可能不是其添加到队列的顺序, 因为OperationQueue
会找到队列中当前最前面的一个处于ready
状态的Operation
来执行(如果前面 3 个都是pending
, 而第四个是ready
, 则会先调度执行第四个).
这样可能导致任务执行顺序混乱, 解决方法是利用任务间的依赖关系来指定严格的执行顺序, 并且这样的依赖关系并不仅限在单一的队列中(即一个队列中的任务可以依赖另外一个队列中的任务执行成功后再执行).
1 |
|
在设置任务间依赖时要注意避免产生死锁, 如果 A 依赖 B, B 依赖 A, 则二者均在等待对方执行完成, 这时就会产生死锁.
在实际开发过程中, 可以利用依赖实现很多常见开发任务, 比如登录的时候首先需要获取公钥, 然后加密密钥, 然后进行登录, 登录成功后再跳转. 这时就可以定义任务 A 是获取公钥, 任务 B 是加密密码, 任务 C 是登录. 建立 A–>B–>C 这样的依赖关系, 就不用再用回调地狱来处理了.
任务的执行结果可以是成功或失败, 但只要是有了执行结果, 任务状态就是
finished
.
在实际工作中如何选择使用
Operation
还是GCD
呢? 实际上它们二者各自有各自适合的使用场景. 针对轻量级的场景, 则使用 GCD, 针对重型任务打包处理, 则使用 Operation.
Beyond the Basics(进阶)
BlockOperation
它是 Operation
的具体子类:
1 |
|
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 中去执行. 利用任务间的依赖, 可以很容易实现复杂逻辑, 或是定义复杂行为.