Farlanki.

Advanced NSOperactions

字数统计: 1.3k阅读时长: 5 min
2016/07/07 Share

Advanced NSOperactions 是WWDC 2015中的一个session.它介绍了一种利用NSOperation分割程序的方法.利用这个方法把一些基本的操作都封装成operation,可以让为程序解耦,同时很好的实现了Don’t Repeat Yourself原则.session中还介绍了一个Demo App.下面是以Demo App为基础对Advanced NSOperactions的一些理解.

OperationCondition

OperationCondition这个协议定义了实现Condiction必须实现的方法和变量.其中包括:

  • name
  • isMutuallyExclusive : 指示排他性
  • dependencyForOperation(operation: Operation) -> NSOperation? 返回一个执行后能满足condiction的operation
  • evaluateForOperation(operation: Operation, completion: OperationConditionResult -> Void) -> 评估condition是否被满足,根据情况调用 completion(.Satisfied) 或者 completion(.Failed(error)).

app中为常用的情况定义了condiction,例如网络连接性,iCloud可用性,Passbook可用性,Location可用性等.

Wrapper

在这些condiction中有两个比较特殊的condiction,我称之为wrapper.它们分别是SilentConditionNegatedCondition.

顾名思义,SilentCondition的功能是把一个condiction的dependencyForOperation函数重载了,返回nil.这样就会导致被包装的condiction不能执行原本自身的dependencyForOperation()方法返回的operation,相应的打开网络,打开定位之类的提示就不会出现,达到了”slient”的效果.

1
2
3
4
func dependencyForOperation(operation: Operation) -> NSOperation? {
// Returning nil means we will never a dependency to be generated.
return nil
}

NegatedCondition针对的是Condiction的evaluateForOperation()方法.它会在condiction不满足的时候调用 completion(.Failed(error)) , 而在满足的时候调用 completion(.Satisfied) , 达到了反转condiction的效果.例如,有一些operation需要网络不成功作为其condiction,就可以把代表网络连接的condiction包装在这个NegatedCondition中.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
func evaluateForOperation(operation: Operation, completion: OperationConditionResult -> Void) {
condition.evaluateForOperation(operation) { result in
if result == .Satisfied {
// If the composed condition succeeded, then this one failed.
let error = NSError(code: .ConditionFailed, userInfo: [
OperationConditionKey: self.dynamicType.name,
self.dynamicType.negatedConditionKey: self.condition.dynamicType.name
])

completion(.Failed(error))
}
else {
// If the composed condition failed, then this one succeeded.
completion(.Satisfied)
}
}
}

observer

observer的作用是监听operation的动作,在operation进入不同的状态的时候会调用observer对应的方法

  • func operationDidStart(operation: Operation) : operation开始执行
  • operation(operation: Operation, didProduceOperation newOperation: NSOperation) : operation 产生了另一个operation
  • func operationDidFinish(operation: Operation, errors: [NSError]) : operation完成执行

operation

operation是NSOperation的子类,它有以下几个新特性.

  • 添加了Condiction
  • 添加了 Observer
  • 在对应的时候通知 Observer

大部分操作都能封装成一个operation,包括下载,更新tableview,转换viewcontroller等.

operation必须重载execute()方法,必须在execute()中调用其中一个finish()方法.

OperationQueue

OperationQueue是NSOperationQueue的子类,这个类实现了以下这些超类没有的特性:

  • queue中任何一个operation完成时通知delegate
  • 提取operation conditions的依赖
  • 设置依赖,实现operation的互斥
    OperationQueue重载了两个方法:
  • addOperation(operation: NSOperation)
  • addOperations(operations: [NSOperation], waitUntilFinished wait: Bool)
  • 1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    override func addOperation(operation: NSOperation) {
    if let op = operation as? Operation {
    // 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)

    // 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)
    }

    /*
    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)
    })
    }

    /*
    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 {
    /*
    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: [])
    }
    }

    delegate?.operationQueue?(self, willAddOperation: operation)
    super.addOperation(operation)
    }

在addOperation(operation: NSOperation)这个方法中,先判断operation是否继承自NSOperation的Operation类型,若是,添加一个blockObserver观察该operation,调用delegate的对应方法.然后对每一个operation,如果该operation有condiction,取得能满足condiction的operation,并将这些operation加入到queue中,设置这些operation作为目标operation的依赖.这样,对于添加了condiction的operation,所有操作都能自动进行.在condiction的operation中会进行判断,如果condiction已经被满足,调用finish(),否则进行操作.

ExclusivityController

ExclusivityController管理一个字典,里面存放着互斥类型和operation:private var operations: [String: [Operation]] = [:]
如果一个operation拥有isMutuallyExclusive为真的condiction,在其加入operation queue的时候,会让其依赖于具有同类互斥性的已经加入queue的operation,具体方法是从ExclusivityControlle管理的字典中取得该种互斥的operation数组,把数组的最后一个operation添加为该operation的依赖,并且把该operation放入数组.

1
2
3
4
5
6
7
8
9
10
11
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
}
category operations
“category 1” operation 1 , operation 2
“category 2” operation 3 , operation 4

当新进入queue的operation 5 的condiction的exclusive的category为 “category1”,把operation 2添加为 operation 5的dependency,并且加入字典.

category operations
“category 1” operation 1 , operation 2 , operation 5
“category 2” operation 3 , operation 4

在一个operation将要被执行的时候,将他移除出数组,这样,通过添加dependency,就可以保证一次仅有一个该种互斥性的operation进行.

CATALOG
  1. 1. OperationCondition
    1. 1.1. Wrapper
  2. 2. observer
  3. 3. operation
  4. 4. OperationQueue
  5. 5. ExclusivityController