Advanced NSOperation 源代码分析(一)

上周末,蜗居在大理创业的大牛 @Howard 老师突然发来微信说这周都在西安和 ThoughtWorks 谈合作的事情,周末是否有空出来一聚。遂定下时间地点,到了周日,大家一起出来在西电后面烤肉摊撸了个串,当时还来了几个西安本土创业团队的小伙伴,从技术话题到创业那些事,相谈甚欢。聚会完毕已经九点多了,创业的小伙伴们表示还要回公司坐一会,真是够拼的。不过人生嘛,梦想还是要有的,万一实现了呢

闲扯完毕,Advanced NSOperation 是 WWDC 2015 的一个 session,看了两遍视频,有点脉络,但还是决定把 sample code 过一遍。Apple 的工程师将所有的逻辑都用 operation 封装组合本来就算一件开脑洞的事情,下面决定先从 Operations 库着手研究一下,没看过视频的同学:先去看视频!*先去看视频!*先去看视频!(重要的事情说三遍)。ps:本人水平有限,有什么错误还望多多指正。

下面是 Operations 库的一个大致结构,本文先从 Operation Queue 着手,毕竟所有的 operation 最终是要提交到 Operation Queue 中来执行。

OperationQueue

OperationQueue 是 NSOperationQueue 子类,首先定义了一个 OperationQueueDelegate 用来 追踪 Operation 生命周期中的一些事件

@objc protocol OperationQueueDelegate: NSObjectProtocol {
    optional func operationQueue(operationQueue: OperationQueue, willAddOperation operation: NSOperation)
    optional func operationQueue(operationQueue: OperationQueue, operationDidFinish operation: NSOperation, withErrors errors: [NSError])
}

OperationQueue 类声明了一个上面定义的 delegate 之外,重写了 func addOperation(operation: NSOperation)func addOperations(operations: [NSOperation], waitUntilFinished wait: Bool) 这两个方法(都是将 operatioin 添加到队列之中)

首先我们来看 func addOperation(operation: NSOperation) 方法:

override func addOperation(operation: NSOperation) {  
    if let op = operation as? Operation {
    //1. Set up a `BlockObserver` to invoke the `OperationQueueDelegate` method.
        let delegate = BlockObserver(startHandler: nil, produceHandler: { 
                    [weak self] in
                    self?.addOperation($1)
                },
                finishHandler: { [weak self] in
                    if let q = self {
                        q.delegate?.operationQueue?(q, operationDidFinish: $0, withErrors: $1)
                    }
                })
        op.addObserver(delegate)

        //2. Extract any dependencies needed by this operation.
        let dependencies = op.conditions.flatMap {
            $0.dependencyForOperation(op)
        }

        for dependency in dependencies {
            op.addDependency(dependency)
            self.addOperation(dependency)
        }

        /* 3. With condition dependencies added, we can now see if this needs
              dependencies to enforce mutual exclusivity.
        */
        let concurrencyCategories: [String] = op.conditions.flatMap { 
            condition in
            if !condition.dynamicType.isMutuallyExclusive { return nil }       
            return "\(condition.dynamicType)"
        }

        if !concurrencyCategories.isEmpty {
            // Set up the mutual exclusivity dependencies.
            let exclusivityController = ExclusivityController.sharedExclusivityController

            exclusivityController.addOperation(op, categories: concurrencyCategories)

            op.addObserver(BlockObserver { operation, _ in
                exclusivityController.removeOperation(operation, 
                    categories: concurrencyCategories)
            })
        }

        /*4.Indicate to the operation that we've finished our extra 
            work on it and it's now it a state where it can proceed with 
            evaluating conditions, if appropriate.
        */
        op.willEnqueue()
    } else {
        /*5.For regular `NSOperation`s, we'll manually call out to the 
            queue's delegate we don't want to just capture "operation" 
            because that would lead to the operation  strongly referencing 
            itself and that's the pure definition of a memory leak.
        */
        operation.addCompletionBlock { [weak self, weak operation] in
            guard let queue = self, let operation = operation else { return }
            queue.delegate?.operationQueue?(queue, operationDidFinish: operation, withErrors: [])
        }
    }
    //6.
    delegate?.operationQueue?(self, willAddOperation: operation)
    super.addOperation(operation)   
}

首先我们先判断添加到 queue 中的 operation 是否是 Operation 类(是我们自定义的 NSOperation 的子类),如果是,则先来配置一番要添加的 operation

  1. 创建一个观察者 BlockObserver 结构体对象,该对象遵守 OperationObserver 协议(主要记录 一个 operation 的开始,执行,和完成这三个阶段),该对象初始化需要带三个参数:

    • startHandler
    • produceHandler
    • finishHandler

    这三个参数分别代表三个 block ,顾名思义,你可以在一个 operation 开始时、执行时、以及完成时在这里写一些关于 OperationQueue 的回调代码。

    创建完观察者对象,将其添加到 operation 的观察者中去

  2. 提取 operation 所有依赖条件,也就是说如果要执行这个 operation,首先查看下有没有必须先执行的先决条件。dependencies 其实是包含 NSOperation 的数组。接着将这些 dependencies 添加到 operation 的依赖中,同样的将这些 dependencies 添加到队列中来,毕竟要先执行完这些依赖才能执行真正的 operation。

  3. 添加完依赖,我们来看看这些依赖是否相互排斥,遍历返回只能独立执行的类名(相互排斥)且放进一个数组中,如果该数组不为空,这里我们创建了一个 ExclusivityController 单例对象,用来追踪所有运行中(队列中)的互斥 operation。
    • 首先我们添加只能独立运行的 operation 到 ExclusivityController 中
    • 接着通过观察者在 operation 完成阶段(finishHandler 里)将其从 ExclusivityController 中移除
  4. 完成上面的设置,执行 op.willEnqueue() 就可以进入就绪状态了。
  5. 对于那些不是 Operation 类的标准的 NSOperation 对象来说,我们手动调用 queue 的 delegate,我们不想捕获 operation,因为那样会引起循环引用。在 complectionBlock 中 我们将手动调用 queue 的 finish delegate 方法。

    addCompletionBlock 是为 NSOperation 添加的一个 extension,其实很简单,如果存在 completion block,则先执行存在的,再执行我们后面添加的:

    func addCompletionBlock(block: Void -> Void) {
        if let existing = completionBlock {
        /*
            If we already have a completion block, we construct a new one by
            chaining them together.
        */
            completionBlock = {
                existing()
                block()
            }
        } else {
            completionBlock = block
        }
    }
    
  6. 完成对 operation 配置后,调用 queue 的 delegate 方法,最后调用 queue 的 super 方法来添加 operation

下面我们重写添加 operations 到 queue 的另一个方法:

override func addOperations(operations: [NSOperation], waitUntilFinished wait: Bool) {  
    /* The base implementation of this method does not call addOperation(),
       so we'll call it ourselves.
    */
    for operation in operations {
        addOperation(operation)
    }

    if wait {
        for operation in operations {
            operation.waitUntilFinished()
        }
    }
}

我们先要调用自己的 addOperation 方法,如果 wait 为 true,则每个 operation 都会以阻塞当前线程的方式执行。

BlockObserver

下面我们来看一下 addOperation(operation: NSOperation) 方法中第 1 步提到的 BlockObserver 对象,这其实是一个结构体对象,遵循 OperationObserver 协议,该协议也很简单:

protocol OperationObserver {

    /// Invoked immediately prior to the `Operation`'s `execute()` method.
    func operationDidStart(operation: Operation)

    /// Invoked when `Operation.produceOperation(_:)` is executed.
    func operation(operation: Operation, didProduceOperation newOperation: NSOperation)

    /*
     Invoked as an `Operation` finishes, along with any errors produced 
     during execution (or readiness evaluation).
    */
    func operationDidFinish(operation: Operation, errors: [NSError])

}

分别在 Operation 执行前,执行中和执行完成后被调用,这样的话 BlockObserver 就其实就简单多了

struct BlockObserver: OperationObserver {  
    // MARK: Properties

    private let startHandler: (Operation -> Void)?
    private let produceHandler: ((Operation, NSOperation) -> Void)?
    private let finishHandler: ((Operation, [NSError]) -> Void)?

    init(startHandler: (Operation -> Void)? = nil, produceHandler: ((Operation, NSOperation) -> Void)? = nil, finishHandler: ((Operation, [NSError]) -> Void)? = nil) {
        self.startHandler = startHandler
        self.produceHandler = produceHandler
        self.finishHandler = finishHandler
    }

    // MARK: OperationObserver

    func operationDidStart(operation: Operation) {
        startHandler?(operation)
    }

    func operation(operation: Operation, didProduceOperation newOperation: NSOperation) {
        produceHandler?(operation, newOperation)
    }

    func operationDidFinish(operation: Operation, errors: [NSError]) {
        finishHandler?(operation, errors)
    }
}

startHandlerproduceHandlerfinishHandler 分别对应了 OperationObserver 协议的三个方法。

TimeoutObserver

TimeoutObserver 是一个实现了 OperationObserver 协议的结构体对象,主要目的是使 Operation 超时后还未完成的话可以自动取消掉,也非常简单。

struct TimeoutObserver: OperationObserver {  
    // MARK: Properties

    static let timeoutKey = "Timeout"

    private let timeout: NSTimeInterval

    // MARK: Initialization
    // 初始化接收一个 timeout 参数
    init(timeout: NSTimeInterval) {
        self.timeout = timeout
    }

    // MARK: OperationObserver

    func operationDidStart(operation: Operation) {
        // When the operation starts, queue up a block to cause it to time out.
        let when = dispatch_time(DISPATCH_TIME_NOW, Int64(timeout * Double(NSEC_PER_SEC)))

        dispatch_after(when, dispatch_get_global_queue(QOS_CLASS_DEFAULT, 0)) {
            /*
                Cancel the operation if it hasn't finished and hasn't already 
                been cancelled.
                到时间后,如果没有 finished 或还没有被 cancel,那么就 cancel
            */
            if !operation.finished && !operation.cancelled {
                let error = NSError(code: .ExecutionFailed, userInfo: [
                    self.dynamicType.timeoutKey: self.timeout
                ])
                // 回传错误原因:被 cancel 是因为超时了
                operation.cancelWithError(error)
            }
        }
    }

    func operation(operation: Operation, didProduceOperation newOperation: NSOperation) {
        // No op.
    }

    func operationDidFinish(operation: Operation, errors: [NSError]) {
        // No op.
    }
}

ExclusivityController

前面提到了 ExclusivityController 是一个单例对象,保持着对队列中所有 排他性Operation 实例的追踪:

class ExclusivityController {  
    //1.
    static let sharedExclusivityController = ExclusivityController()

    private let serialQueue = dispatch_queue_create("Operations.ExclusivityController", DISPATCH_QUEUE_SERIAL)
    private var operations: [String: [Operation]] = [:]

    private init() {
        /*
            A private initializer effectively prevents any other part of the app
            from accidentally creating an instance.
        */
    }

    ///2. Registers an operation as being mutually exclusive
    func addOperation(operation: Operation, categories: [String]) {
        /*
            This needs to be a synchronous operation.
            If this were async, then we might not get around to adding dependencies 
            until after the operation had already begun, which would be incorrect.
        */
        dispatch_sync(serialQueue) {
            for category in categories {
                self.noqueue_addOperation(operation, category: category)
            }
        }
    }

    ///3. Unregisters an operation from being mutually exclusive.
    func removeOperation(operation: Operation, categories: [String]) {
        dispatch_async(serialQueue) {
            for category in categories {
                self.noqueue_removeOperation(operation, category: category)
            }
        }
    }
    ......
}
  1. 首先 ExclusivityController 是个单例,我们这里只初始化一次,然后创建了一个串行队列和一个字典对象
  2. 将 operation 和 他所对应的所有互斥性依赖(类名)添加进 ExclusivityController 的字典之中,注意这里用的是同步操作
  3. 将 operation 和 他所对应的互斥性依赖(类名)从字典中移除

这里调用了两个私有方法来具体实现:

// MARK: Operation Management
    //1.
    private func noqueue_addOperation(operation: Operation, category: String) {

        var operationsWithThisCategory = operations[category] ?? []

        if let last = operationsWithThisCategory.last {
            operation.addDependency(last)
        }

        operationsWithThisCategory.append(operation)
        operations[category] = operationsWithThisCategory
    }
    //2.
    private func noqueue_removeOperation(operation: Operation, category: String) {

        let matchingOperations = operations[category]

        if var operationsWithThisCategory = matchingOperations,
           let index = operationsWithThisCategory.indexOf(operation) {
            operationsWithThisCategory.removeAtIndex(index)
            operations[category] = operationsWithThisCategory
        }
    }
  1. 首先来看下 func noqueue_addOperation ,由于 ExclusivityController 对象是一个单例,因此通过不断的调用此方法,可以将所有的 operation 对应的互斥依赖都添加到字典中。之后再有新的 operation 也有同样的互斥依赖,可以将之前的 operation 添加为自己的依赖,这里说的很抽象,还是画一副图来看看吧

    第一次传入 operation1 和 对应的互斥依赖(数组 categorys),经过 func noqueue_addOperation方法,生成 operations 字典对象。记住 ExclusivityController 是单例对象,operations 的生存周期为整个程序生命期。

    如果再次传入的 operation2 也有与 operation 1 同样的互斥依赖(c2,c3),这个时候先从 operations 字典中取出 operation1 ,然后通过 operation2.addDependency(operation1) 方法添加依赖,接着对字典 operations 进行替换 接着将 operation2 添加到数组(绿色部分),再将数组赋值到字典(多谢 @summer-liu 指正)

  2. func noqueue_removeOperation 刚好是一个相反的过程,移除指定的 operation 对象,很简单就不一一表述了


-EOF-

如果感觉此文对你有帮助,请随意打赏支持作者 😘

chengway

认清生活真相之后依然热爱它!

Subscribe to Talk is cheap, Show me the world!

Get the latest posts delivered right to your inbox.

or subscribe via RSS with Feedly!