前言
NSOperation是Foundation中用于多线程并发任务的抽象类,它可以放到NSOperationQueue任务队列中去执行。它底层还是基于GCD来实现的,相比GCD它支持取消操作和依赖管理。
Apple用swift重写了Foundation库并且开源,可以通过阅读NSOperation的swift源代码来了解其实现原理。
https://github.com/apple/swift-corelibs-foundation/blob/main/Sources/Foundation/Operation.swift 点击跳转
在分析源代码之前也请阅读一下苹果的《Concurrency Programming Guide》的Operation Queues一章,要了解如何使用Operation。
同步和异步
NSOperation是抽象类,使用它需要子类继承它,并覆写一些成员方法。
它有两种模式,一种是同步执行,一种是异步执行。
同步执行,需要覆写main方法即可。
@interface MyOperation : NSOperation
@end
@implementation MyOperation
- (void)main {
// 要实现的业务逻辑写这里即可
}
@end
当main方法返回的时候,任务也就结束了。
异步执行,需要覆写start方法,同时实现asynchronous、concurrent、executing和finished属性。
@interface MyOperation : NSOperation {
BOOL _executing;
BOOL _finished;
}
@property (nonatomic, assign, getter = isExecuting) BOOL executing;
@property (nonatomic, assign, getter = isFinished) BOOL finished;
@end
@implementation MyOperation
@synthesize executing = _executing;
@synthesize finished = _finished;
- (void)start {
// 要实现的业务逻辑写这里即可
}
- (BOOL)isAsynchronous {
return YES;
}
- (BOOL)isConcurrent {
return YES;
}
- (void)setFinished:(BOOL)finished {
[self willChangeValueForKey:@"isFinished"];
_finished = finished;
[self didChangeValueForKey:@"isFinished"];
}
- (void)setExecuting:(BOOL)executing {
[self willChangeValueForKey:@"isExecuting"];
_executing = executing;
[self didChangeValueForKey:@"isExecuting"];
}
@end
当start方法返回的时候,任务可以继续执行,直到任务真正执行完毕,我们执行self.finished=YES; self.executing=YES;通过KVO的方式通知NSOperation任务已经执行完毕,可以从NSOperationQueue中移除此任务了。
OperationQueue
OperationQueue是任务的执行队列
初始化
OperationQueue有两个初始化方法:
一个是public方法给大家创建任务队列使用的
open class OperationQueue : NSObject, ProgressReporting {
// 任务队列名字
var __name: String?
// 对外的初始化方法
public override init() {
super.init()
__name = "NSOperationQueue \(Unmanaged<OperationQueue>.passUnretained(self).toOpaque())"
}
}
另一个是internal方法,专门用来初始化mainQueue的
extension OperationQueue {
public static let defaultMaxConcurrentOperationCount: Int = -1
}
open class OperationQueue : NSObject, ProgressReporting {
// 是否是主线程队列
var __mainQ: Bool = false
// 并发数上限
var __maxNumOps: Int = OperationQueue.defaultMaxConcurrentOperationCount
// 实际并发数上限
var __actualMaxNumOps: Int32 = .max
// 任务队列名字
var __name: String?
// QOS,任务队列的优先级
var __propertyQoS: QualityOfService?
// asMainQueue是一个空元组,只是为了重载init方法,方便内部使用
internal init(asMainQueue: ()) {
super.init()
__mainQ = true
__maxNumOps = 1
__actualMaxNumOps = 1
__name = "NSOperationQueue Main Queue"
#if canImport(Darwin)
__propertyQoS = QualityOfService(qos_class_main())
#else
__propertyQoS = QualityOfService.userInteractive
#endif
}
// 返回主线程任务队列
open class var main: OperationQueue {
get {
struct Once {
// 使用static保证了变量只初始化一次
static let mainQ = OperationQueue(asMainQueue: ())
}
return Once.mainQ
}
}
}
添加任务
添加一个Operation到任务队列中,对外提供了两个方法,可以添加单个任务或者批量任务。
open class OperationQueue : NSObject, ProgressReporting {
// 添加单个任务
open func addOperation(_ op: Operation) {
_addOperations([op], barrier: false)
}
// 添加多个任务,同时支持设置是否同步等待
open func addOperations(_ ops: [Operation], waitUntilFinished wait: Bool) {
_addOperations(ops, barrier: false)
if wait {
for op in ops {
op.waitUntilFinished()
}
}
}
}
对内提供了一个下划线开头的批量添加任务方法,所有的添加任务,都最终会走到此方法中
open class OperationQueue : NSObject, ProgressReporting {
internal func _addOperations(_ ops: [Operation], barrier: Bool = false) {
// 判空保护
if ops.isEmpty { return }
// 失败数
var failures = 0
// 成功数
var successes = 0
// 第一个新任务
var firstNewOp: Unmanaged<Operation>?
// 上一个新任务
var lastNewOp: Unmanaged<Operation>?
// 循环传入的任务数组
for op in ops {
// 对op的状态进行CAS修改,从initialized修改成enqueuing
if op._compareAndSwapState(.initialized, .enqueuing) {
// 如果修改成功,成功数累加1
successes += 1
// 判断之前的修改是否都成功
if 0 == failures {
// 走到这里,说明所有的任务的状态都正确修改成e