更新整理自: iOS 并发编程之 Operation Queues http://blog.leichunfeng.com/blog/2015/07/29/ios-concurrency-programming-operation-queues/
基本概念
进程 (Process): 一个正在运行中的可执行文件. 每一个进程都有独立的内存空间和系统资源(端口权限等), 至少包含一个主线程和任意数量的其他(辅) 线程. 当一个进程的主线程退出时, 则该进程即结束了.
线程(Thread): 一个独立的代码执行路径, 也即线程是代码执行路径的最小分支. iOS 中, 线程底层基于 POSIX threads APIs, 即 pthreads.
任务(Task): 需要执行的工作(一段代码), 是个抽象概念.
串行 vs. 并发: 主要区别在于允许同时执行的任务数量
串行: 一次只能执行一个任务, 必须等一个任务执行完成后才执行下一个任务.
并发: 指允许多个任务同时执行.(note: 其实也是某一时刻只执行一个任务, 系统在多个任务之间进行快速切换, 区别于并行)
同步 vs. 异步: 主要区别在于是否等待任务执行完成, 即是否阻塞当前线程.
同步: 会等待执行完成后再继续执行接下来的代码
异步: 调用后立即返回, 不会等待执行操作的执行结果
队列 vs. 线程:
队列: iOS 中, 分为串行队列与并发队列, 用于处理不同需要的任务.
线程: iOS 系统使用队列进行任务调度, 根据任务需要和系统负载情况动态地创建和销毁线程, 而无须手动管理.
iOS 的并发编程模型
在 iOS 中, 苹果与传统的基于线程不同, 而是采用队列, 一般只需要定义好调度的任务, 并加入到相应的队列, 系统就会在合适的线程执行这些任务, 而不需要关心线程的创建和销毁.
以下情景应该直接使用线程:
用线程以外的方式无法实现的特定任务
必须实时执行一个任务
对在后台执行的任务有更多的可预测行为
Operation Queues vs. Grand Central Dispatch (GCD)
Operation Queues: 相对增加了一点开销, 更灵活, 可通过给 operation 之间添加依赖关系, 开始, 暂停, 恢复, 或取消 operation
GCD: 轻量级, 以 FIFO 的顺序执行并发任务. 使用 GCD 时, 我们不关心任务具体的调度情况, 而是由系统处理, 因此也相对不够灵活
关于 Operation 对象
用于封装需要执行的任务, Operation 本身是一个抽象类, 使用时必须创建自定义子类或使用系统预定义的子类, NSInvocationOperation 和 BlockOperation
NSInvocationOperation: 通过一个 object 和 selector 创建一个非并发的(non-concurrent) operation(Swift 不可用, BlockOperation 或 OperationQueue.addOperation(block:) 替代)
BlockOperation: 可用来并发执行一个或多个 block, 只有当一个 BlockOperation 关联的所有 block 执行完毕, 这个 operation 才算执行完毕, 类似 dispatch_group
另外, 所有 operation 都支持以下特性:
支持在 operation 之间建立依赖关系, 只有当一个 operation 所依赖的所有 operation 都完成时, 这个 operation 才能开始执行;
支持一个可选的 completion block, 会在主任务执行完成时被调用;
支持通过 KVO 来观察 operation 执行状态的变化;
支持设置执行的优先级, 从而影响 operation 之间的相对执行顺序;
支持取消, 可以停止正在执行的 operation
并发 vs. 非并发 Operation
一般都是通过将 operation 添加到 operation queue 的方式来执行 operation, 但这并不是必须的, 还可以直接调用 start 方法来执行, 但这种方式不能保证 operation 是异步执行的. Operation 类的 isConcurrent 方法的返回值标识了相对于调用 start 方法的线程是否异步执行, 默认情况下, isConcurrent 的返回值为 false, 即会阻塞调用 start 方法的线程.
自定义并发执行的 operation, 需要编写一些额外的代码支持异步执行, 如创建新线程, 调用系统的异步方法或其他方式确保 start 方法在开始执行任务后立即返回. 但绝大多数情况下, 都是将 operation 添加到 operation queue 的方式来执行, 因此没必要实现并发的 operation.
创建 NSInvocationOperation
Swift 不可用, 略.
创建 BlockOperation
- let blockOperation = BlockOperation(block: {
- print("start excute block1")
- sleep(1)
- print("finish block1")
- })
blockOperation.addExecutionBlock {
- print("start excute block2")
- sleep(1)
- print("finish block2")
- }
blockOperation.addExecutionBlock {
- print("start excute block3")
- sleep(1)
- print("finish block3")
- }
- blockOperation.start()
* 异步执行, blocks 开始与结束的顺序不确定
自定义 Operation 对象
非并发 Operation 子类
执行 main 方法中的主任务
响应取消事件:
尽管 operation 是支持取消操作的, 但却并不是立即取消的, 而是在你调用了 operation 的 cancel 方法之后的下一个 isCancelled 的检查点取消的.
并发 Operation 子类额外配置:
重写 start 方法, 并且一定不要调用父类的实现
重写 isExecuting 和 isFinished, 维护这两个状态, 并生成响应的 KVO 通知
重写 isConcurrent, 返回 true.
注意! 即使一个 operation 是被 cancel 掉了, 仍需手动触发 isFinished 的 KVO 通知. 因为 operation 依赖会观察其 isFinished 值变化.
维护 KVO 通知
Operation 类以下 Key Path 支持 KVO 通知:
isCancelled
isConcurrent
isExecuting
isFinished
isReady
dependencies
queuePriority
completionBlock
定制 Operation 对象的行为
Operation 的灵活性就体现在执行行为的可定制
配置 (单向) 依赖关系
addDependency(op:)
removeDependency(op:)
与 operation queue 无关
注意! 不要循环依赖, 否则永远不会执行
在手动执行或添加到 operation queue 之前配置好依赖关系, 否则可能会失效
修改 Operation 在队列中的优先级
.queuePriority
第一要素是 operation 的 isReady 状态(取决于依赖关系)
其次即 operation 在队列中的优先级
默认优先级 normal
应用于相对的 operation queue
修改 Operation 在线程中的优先级(iOS 8.0 废弃, 新增 qualityOfService 替代)
.threadPriority
默认的 start 方法会修改它的线程优先级
只影响 main 方法执行所在线程
自定义并发 operation 子类时在 start 方法中也要根据指定的值修改当前线程的优先级
设置 Completion Block
.completionBlock
注意! operation 被取消时, completion block 也会执行
无法保证在主线程执行
执行 Operation
将 operation 添加到一个 operation queue, 自动执行
创建 OperationQueue 对象实例
添加 operation
addOperation(op: Operation)
addOperations(ops: [Operation], waitUntilFinished: Bool)
addOperation(block: )
operation 添加到 operation queue 了就不要再修改了
直接调用 start 方法手动执行
执行前应该做些防范性判断
isReady
isCancel
取消 Operation
operation 加入 operation queue 后, 唯一 dequeue 的办法就是调用 operation 的 cancel 方法
等待 Operation 结束
OperationQueue 的 addOperations(ops: [Operation], waitUntilFinished: Bool) 和 Operation 的 waitUntilFinished 方法用于阻塞当前线程, 直到相关 operation 执行结束.
注意! 避免阻塞主线程.
暂停和恢复 Operation Queue
暂停队列并不能暂停正在执行的 operation, 只是暂停调度新的 operation
更多参考
https://www.raywenderlich.com/76341/use-nsoperation-nsoperationqueue-swift
关注下面的标签, 发现更多相似文章
iOS
Apple
Swift
安装掘金浏览器插件
来源: https://juejin.im/post/5ac8628f518825364001aeec