本文详细介绍了在实现同步、异步 NSOperation 时分别须要实现哪些方法、注意哪些问题。最后对 GCD 与 NSOperation Queue 做了一个简单的对比。html
本文同时发表于个人我的博客ios
在 iOS 中实现并发编程主要有三种方式:GCD、NSOperation Queue以及Thread,其中前二者使用普遍。 在正式开始以前有必要区分两组概念:同步、异步与串行、并行。git
dispatch_sync
),异步方法则是将任务抛出去,在任务没完成前就返回(如:dispatch_async
);NSOperation Queue + NSOperation 做为 iOS 中『高级的、面向对象的并发编程方式』耳熟能详,但具体到一些细节问题上认识每每又比较模糊。本文在苹果官方文档 Concurrency Programming Guide、NSOperation Class Reference 以及 NSOperationQueue Class Reference 的基础上作了一次疏理和总结。github
NSOperation
自己是个抽象类,在使用前必须子类化(系统预约义了两个子类:NSInvocationOperation
和 NSBlockOperation
)。那问题来了,在子类化过程当中,须要重写父类的哪些方法?编程
这首先就要了解一下NSOperation
类中几个重要方法的默认实现: 安全
因为操做 NSOperation 与 NSOperation 任务的执行每每在不一样的线程上进行,在继续以前须要强调线程安全问题:『NSOperation 自己是 thread-safe,当咱们在子类重写或自定义方法时一样须要保证 thread-safe』。网络
对于 Synchronous Operation,在调用其 start
方法的线程上同步执行该 operation 的任务,start
方法返回时 operation 执行完成。所以,对于 Synchronous Operation 通常只需重写 main
方法便可(start
方法的默认实现已实现相关 KVO 功能)。并发
然而对于 Asynchronous Operation,调用其 start
方法后,在 start
返回时 operation 的任务可能还没完成(为了实现异步,通常须要在其余线程执行 operation 的具体任务)。所以 start
方法默认实现不能知足异步须要(默认实现会在start
返回前将 isExecuting
置为 NO、isFinished
置为 YES,并产生 KVO 通知)。此时至少须要重写如下方法:app
start
方法来实现,能够经过建立子线程或其余异步方式完成。同时须要在任务开始前将 isExecuting
置为YES 并抛出 KVO 通知。 『重写的 start
方法必定不能调用 [super start]
』isExecuting
上抛出 KVO 通知isFinished
上抛出 KVO 通知这里咱们看看著名的网络框架 AFNetworking 中关于 NSOperation 的使用:框架
AFNetworking 3.0 全面使用
NSURLSession
,而NSURLSession
自己是异步的、且没有NSURLConnection
须要 runloop 配合的问题,所以在3.0版本中并无使用 NSOperation,代码获得很大的简化。这里咱们说的是 AFNetworking 2.3.1 版本。
在 AFNetworking 中 AFURLConnectionOperation 是个异步的 NSOperation 子类,其 start
方法以下:
start
方法的实现能够看到:
NSURLConnection
的网络回调必需要有 runloop 的配合,经过port-based input source 唤醒 runloop 处理网络事件),也就是说 AFURLConnectionOperation 是在一条常驻子线程中处理网络回调。前面咱们提到 operation 被 cancel 时也被认为是完成,这点在自定义 start
时一样须要注意:
cancelConnection
以及
connection:didFailWithError:
方法中都会调用其
finish
方法:
cancel
方法后该如何处理彻底由咱们自定义的
start
方法决定(固然良好的设计应该要符合 cancel 的语义)。
同时,AFURLConnectionOperation 也实现了如下方法:
dependencies: 咱们能够在 operation 间添加依赖关系,在某个 operation 所依赖的 operations 完成以前,其一直处于未就绪状态(isReady
为 NO)。 须要注意的是,依赖关系是 operation 自身的状态,也就是说有依赖关系的 operations 能够处在不一样的 NSOperationQueue 中。
isReady: isReady
默认实现主要处理 operation 间的依赖关系,当咱们自定义该方法时须要考虑 super
的值,如 AFURLConnectionOperation中关于 isReady
的实现:
qualityOfService: 用于表示 operation 在获取系统资源时的优先级,默认值:NSQualityOfServiceBackground
,咱们能够根据须要给 operation 赋不一样的优化级,如最高优化级:NSQualityOfServiceUserInteractive
。
queuePriority: 用于设置 operation 在 operation queue 中的相对优化级,同一 queue 中优化级高的 operation(isReady
为 YES) 会被优先执行。须要注意区分qualityOfService
(在系统层面,operation 与其余线程获取资源的优先级)与queuePriority
(同一 queue 中 operation 间执行的优化级)的区别。 同时,须要注意dependencies
(严格控制执行顺序)与queuePriority
(queue 内部相对优先级)的区别。
NSOperation Queue 用于管理、执行 NSOperation,不管其中的 operation 是并行仍是串行,queue 都会在子线程(借用 GCD)中执行 operation。 从上小节咱们知道,实现异步 operation 比同步 operation 要复杂许多,所以若是打算将 operation 加入 queue 中,则彻底能够将 operation 实现为同步方式。 对于 queue 中已就绪的 operation,queue 会选择 queuePriority
值最大的 operation 执行。
关于 NSOperation Queue 有两点须要强调:
cancel
方法。(从上小节咱们知道,对 operation 调用 cancel
方法后的效果彻底由 operation 本身决定。cancel
惟一能影响的就是清除 operation 的依赖关系,使其当即能够被执行)。此时 queue 并不会 remove 其中的 operations,remove 操做仅发生在 operation 完成时。GCD 与 NSOperation Queue 做为常见的并发编程方式,在使用时该如何选择? 首先,对比一下咱们关心的几个问题:
别忘了,咱们还有第三种选择:NSThread。因为使用 NSThread 时须要处理线程相关的问题,通常不多使用。但不管是 GCD 仍是 NSOperation Queue,其中的任务具体什么时候执行是由系统控制的,对于实时性要求很高的任务则可使用 NSThread。
本文简单讨论了在使用 NSOperation 时须要重写哪些方法、注意哪些问题。同时也对 GCD 与 NSOperation Queue 做了简单对比,在清楚了它们各自的特色以后再作选择时会更加清晰。