在日常使用中,特别是在第三方框架中,总会看见NSOperation,今天分别从OC和Swift底层探究一下它:
(一)NSOperation —— OC
看看下面例子:
NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"1 - %@", [NSThread currentThread]);
}];
NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"2 - %@", [NSThread currentThread]);
}];
NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"3 - %@", [NSThread currentThread]);
}];
//添加依赖
[op3 addDependency: op1];
[op3 addDependency: op2];
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
queue.maxConcurrentOperationCount = 3;
//添加后自动调用
[queue addOperation: op1];
[queue addOperation: op2];
[queue addOperation: op3];
结果:op3
一定会在op1
和op2
完成后执行。
- 从GNUstep源码探究
NSOperation
的底层,首先看添加依赖:
@implementation NSOperation
...
- (void) addDependency: (NSOperation *)op
{
...
NS_DURING
{
if (NSNotFound == [internal->dependencies indexOfObjectIdenticalTo: op])
{
[self willChangeValueForKey: @"dependencies"];
[internal->dependencies addObject: op];//添加到依赖队列
if (NO == [op isFinished]
&& NO == [self isCancelled]
&& NO == [self isExecuting]
&& NO == [self isFinished])
{
[op addObserver: self
forKeyPath: @"isFinished"
options: NSKeyValueObservingOptionNew
context: isFinishedCtxt];//监听-完成
if (internal->ready == YES)//默认是yes
{
[self willChangeValueForKey: @"isReady"];
internal->ready = NO;//变为no
[self didChangeValueForKey: @"isReady"];
}
}
[self didChangeValueForKey: @"dependencies"];
}
}
...
}
把op1,2
添加到dependencies
中,并且对它的isFinished
进行监听,然后op3
修改ready
为NO
。
- 接着把所有
NSOperation
添加到NSOperationQueue
中:
@implementation NSOperationQueue
...
- (void) addOperation: (NSOperation *)op
{
...
if (NSNotFound == [internal->operations indexOfObjectIdenticalTo: op]
&& NO == [op isFinished])
{
[op addObserver: self
forKeyPath: @"isReady"
options: NSKeyValueObservingOptionNew
context: isReadyCtxt];//监听-准备就绪
[self willChangeValueForKey: @"operations"];
[self willChangeValueForKey: @"operationCount"];
[internal->operations addObject: op];//添加到要执行的队列
[self didChangeValueForKey: @"operationCount"];
[self didChangeValueForKey: @"operations"];
if (YES == [op isReady])
{
[self observeValueForKeyPath: @"isReady"
ofObject: op
change: nil
context: isReadyCtxt];//即时更新状态
}
}
[internal->lock unlock];
}
首先监听NSOperation
的isReady
,并且添加到operations
中,然后手动调用KVO
的监听回调。
- 这时候会先移除对
isReady
的监听,因为只监听一次。然后把NSOperation
添加到waiting
中, 最终调用_execute
时会取出来:
@implementation NSOperationQueue (Private)
- (void) observeValueForKeyPath: (NSString *)keyPath
ofObject: (id)object
change: (NSDictionary *)change
context: (void *)context
{
/* We observe three properties in sequence ...
* isReady (while we wait for an operation to be ready)
* queuePriority (when priority of a ready operation may change)
* isFinished (to see if an executing operation is over).
*/
if (context == isFinishedCtxt)
{ ... }
else if (context == queuePriorityCtxt || context == isReadyCtxt)
{
...
if (context == isReadyCtxt)//如果任务准备就绪
{
[object removeObserver: self forKeyPath: @"isReady"];//移除监听-准备
[object addObserver: self
forKeyPath: @"queuePriority"
options: NSKeyValueObservingOptionNew
context: queuePriorityCtxt];//监听-队列优先级
}
pos = [internal->waiting insertionPosition: object
usingFunction: sortFunc
context: 0];
[internal->waiting insertObject: object atIndex: pos];//插入
[internal->lock unlock];
}
[self _execute];//执行
}
-
NSOperation
从waiting
取出来后,就被移除了。然后监听NSOperation
的isFinished
,便开始start
(所以NSOperation
添加到NSOperationQueue
中会自动调用start
)。
@implementation NSOperationQueue (Private)
...
- (void) _execute
{
...
while (NO == [self isSuspended]
&& max > internal->executing
&& [internal->waiting count] > 0)
{
NSOperation *op;
op = [internal->waiting objectAtIndex: 0];//取出
[internal->waiting removeObjectAtIndex: 0];//移除
[op removeObserver: self forKeyPath: @"queuePriority"];//移除队列优先级监听
[op addObserver: self
forKeyPath: @"isFinished"
options: NSKeyValueObservingOptionNew
context: isFinishedCtxt];//监听完成
internal->executing++;//计数加1
if (YES == [op isConcurrent])//是否并发
{
[op start];//执行
}
else { ... }
}
[internal->lock unlock];
}
-
start
其实就是修改一下executing
,然后调用_finish
:
@implementation NSOperation
...
- (void) start
{
...
if (NO == internal->executing)//是否在执行
{
[self willChangeValueForKey: @"isExecuting"];//手动通知-kvo
internal->executing = YES;//start的作用主要是修改状态
[self didChangeValueForKey: @"isExecuting"];
}
...
[self _finish];//也只是修改状态
[pool release];
}
-
_finish
也只是修改executing
和finished
:
@implementation NSOperation (Private)
- (void) _finish
{
[self retain];
[internal->lock lock];
if (NO == internal->finished)
{
if (YES == internal->executing)
{
[self willChangeValueForKey: @"isExecuting"];
[self willChangeValueForKey: @"isFinished"];
internal->executing = NO;
internal->finished = YES;
[self didChangeValueForKey: @"isFinished"];
[self didChangeValueForKey: @"isExecuting"];
}
else
{ ... }
...
}
- 当
finished
被修改后,便会监听回调:
@implementation NSOperationQueue (Private)
- (void) observeValueForKeyPath: (NSString *)keyPath
ofObject: (id)object
change: (NSDictionary *)change
context: (void *)context
{
if (context == isFinishedCtxt)
{
[internal->lock lock];
internal->executing--;//正在执行的任务计数减一
[object removeObserver: self forKeyPath: @"isFinished"];//移除监听-完成
...
[internal->operations removeObjectIdenticalTo: object];//移除任务
...
}
else if (context == queuePriorityCtxt || context == isReadyCtxt)
{ ... }
[self _execute];//执行
}
同时,NSOperation
初始化时自身也添加了监听,所以也会回调:
@implementation NSOperation
...
- (id) init
{
if ((self = [super init]) != nil)
{
...
internal->ready = YES;//默认
...
[self addObserver: self
forKeyPath: @"isFinished"
options: NSKeyValueObservingOptionNew
context: isFinishedCtxt];//添加监听-完成
}
return self;
}
@implementation NSOperation
...
- (void) observeValueForKeyPath: (NSString *)keyPath
ofObject: (id)object
change: (NSDictionary *)change
context: (void *)context
{
[internal->lock lock];
[object removeObserver: self forKeyPath: @"isFinished"];//移除监听-完成
...
if (NO == internal->ready)
{
NSEnumerator *en;
NSOperation *op;
en = [internal->dependencies objectEnumerator];
while ((op = [en nextObject]) != nil)//遍历判断依赖的op是否全部完成
{
if (NO == [op isFinished])
break;
}
if (op == nil)
{
[self willChangeValueForKey: @"isReady"];
internal->ready = YES;//设为yes;可以开始启动
[self didChangeValueForKey: @"isReady"];
}
}
[internal->lock unlock];
}
同样是会先移除对isFinished
的监听,然后遍历循环判断是否全部op1,2
都完成。如果都完成,当前op
将为nil
,然后进入判断修改op3
的ready
为YES
。
-
op3
的ready
被修改后,便会重复第3步,最终实现调用。也就是在op1,2
执行完成后才执行的op3
。
总的来说,在OC底层中,使用
KVO
对NSOperation
的各个状态进行监听,从而进行优先级调用。
(二)NSOperation —— Swift
看看下面例子:
let op1 = BlockOperation.init {
print("1 - \(Thread.current)")
}
let op2 = BlockOperation.init {
print("2 - \(Thread.current)")
}
let op3 = BlockOperation.init {
print("3 - \(Thread.current)")
}
//添加依赖
op3.addDependency(op1)
op3.addDependency(op2)
let queue = OperationQueue.init()
queue.maxConcurrentOperationCount = 3
//添加后自动调用
queue.addOperation(op1)
queue.addOperation(op2)
queue.addOperation(op3)
结果:op3
一定会在op1
和op2
完成后执行。
- 从swift-corelibs-foundation源码探究
Operation
的底层,首先看添加依赖:
open class Operation : NSObject {
...
internal func _addDependency(_ op: Operation) {
withExtendedLifetime(self) {
withExtendedLifetime(op) {
var up: Operation?
_lock()
if __dependencies.first(where: { $0 === op }) == nil {
__dependencies.append(op)//添加到依赖序列
up = op
}
_unlock()
if let upwards = up {
upwards._lock()
_lock()
let upIsFinished = upwards._state == __NSOperationState.finished
if !upIsFinished && !_isCancelled {
assert(_unfinishedDependencyCount >= 0)
_incrementUnfinishedDependencyCount()//计数加1
upwards._addParent(self)//反过来被添加
}
_unlock()
upwards._unlock()
}
Operation.observeValue(forKeyPath: _NSOperationIsReady, ofObject: self)//手动通知-准备就绪
}
}
}
open func addDependency(_ op: Operation) {
_addDependency(op)
}
...
同样地,op1,2
会被添加到__dependencies
,然后计数,计数会影响isReady
的判断:
open class Operation : NSObject {
...
open var isReady: Bool {
_lock()
defer { _unlock() }
return __unfinishedDependencyCount == 0
}
...
}
最终通过_addParent(self)
,op1,2
会把op3
添加到__downDependencies
中:
open class Operation : NSObject {
...
internal func _addParent(_ parent: Operation) {
__downDependencies.insert(PointerHashedUnmanagedBox(contents: .passUnretained(parent)))//添加
}
...
}
- 最终会手动通知
isReady
状态,但是计数不为0
所以不会调用_schedule()
:
open class Operation : NSObject {
...
internal static func observeValue(forKeyPath keyPath: String, ofObject op: Operation) {
...
if let transition = kind {
switch transition {
case .toFinished: ...
case .toExecuting: ...
case .toReady:
let r = op.isReady//通过计数判断ready
op._cachedIsReady = r
let q = op._queue
if r {
q?._schedule()
}
}
}
}
...
}
- 接着把所有
Operation
添加到OperationQueue
中:
open class OperationQueue : NSObject, ProgressReporting {
...
internal func _addOperations(_ ops: [Operation], barrier: Bool = false) {
...
for op in ops {
if op._compareAndSwapState(.initialized, .enqueuing) {
successes += 1
if 0 == failures {
let retained = Unmanaged.passRetained(op)
op._cachedIsReady = op.isReady
let schedule: DispatchWorkItem
if let qos = op.__propertyQoS?.qosClass {
schedule = DispatchWorkItem.init(qos: qos, flags: .enforceQoS, block: {
self._schedule(op)//执行任务
})
} else { ... }
op._adopt(queue: self, schedule: schedule)//保存任务
...
} else { ... }
} else { ... }
}
...
if !barrier {
_schedule()//执行
}
}
open func addOperation(_ op: Operation) {
_addOperations([op], barrier: false)
}
...
}
然后先把任务包装成DispatchWorkItem
保存:
open class Operation : NSObject {
...
internal func _adopt(queue: OperationQueue, schedule: DispatchWorkItem) {
_lock()
defer { _unlock() }
__queue = Unmanaged.passRetained(queue)
__schedule = schedule//保存任务
}
...
}
- 最后会调用
_schedule()
开始执行任务,并在完成后修改isFinished
,当然op3
并不会执行这步:
open class OperationQueue : NSObject, ProgressReporting {
...
internal func _schedule() {
...
for prio in Operation.QueuePriority.priorities {
...
while let operation = op?.takeUnretainedValue() {
...
if Operation.__NSOperationState.enqueued == operation._state && operation._fetchCachedIsReady(&retest) {
...
if let schedule = operation.__schedule {
if operation is _BarrierOperation {
queue.async(flags: .barrier, execute: {
schedule.perform()
})
} else {
queue.async(execute: schedule)//执行任务
}
}
op = next
} else { ... }
}
}
...
}
...
}
open class OperationQueue : NSObject, ProgressReporting {
...
internal func _schedule(_ op: Operation) {
op._state = .starting
OperationQueue._currentQueue.set(self)
op.start()//执行
OperationQueue._currentQueue.clear()
Unmanaged.passUnretained(self).release()
if op.isFinished && op._state.rawValue < Operation.__NSOperationState.finishing.rawValue {
Operation.observeValue(forKeyPath: _NSOperationIsFinished, ofObject: op)//手动通知-完成
}
}
...
}
open class Operation : NSObject {
...
open func start() {
...
if !isCanc {
_state = .executing
Operation.observeValue(forKeyPath: _NSOperationIsExecuting, ofObject: self)//手动通知-Executing
_queue?._execute(self) ?? main() //调用main
}
if __NSOperationState.executing == _state {
_state = .finishing
Operation.observeValue(forKeyPath: _NSOperationIsExecuting, ofObject: self)//手动通知-Executing
Operation.observeValue(forKeyPath: _NSOperationIsFinished, ofObject: self)//手动通知-完成
} else { ... }
}
...
}
open class Operation : NSObject {
...
internal static func observeValue(forKeyPath keyPath: String, ofObject op: Operation) {
...
if let transition = kind {
switch transition {
case .toFinished: // we only care about NO -> YES
...
let down_deps = op.__downDependencies//取出
op.__downDependencies.removeAll()//移除
...
if 0 < ready_deps.count {
for down in ready_deps {
down._lock()
if down._unfinishedDependencyCount >= 1 {
down._decrementUnfinishedDependencyCount()//计数减1
}
down._unlock()
Operation.observeValue(forKeyPath: _NSOperationIsReady, ofObject: down)//手动通知-准备就绪
}
}
...
case .toExecuting:
let isExecuting = op.isExecuting
op._lock()
if op._state.rawValue < __NSOperationState.executing.rawValue && isExecuting {
op._state = .executing//只是修改状态
}
op._unlock()
case .toReady:
let r = op.isReady//通过计数判断ready
op._cachedIsReady = r
let q = op._queue
if r {
q?._schedule()//执行
}
}
}
}
...
}
最后会手动通知down
的isReady
的变化,而down
就是从__downDependencies
取出的op3
。
- 当
op1,2
都执行完成,计数为0
时,op3
的isReady
便为true
,开始调用_schedule()
执行任务,重复第4步。
总的来说,Swift和OC不一样,没有KVO,只是通过手动通知状态变化,从而进行优先级调用。