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
.它们分别是SilentCondition
和NegatedCondition
.
顾名思义,SilentCondition的功能是把一个condiction的dependencyForOperation函数重载了,返回nil.这样就会导致被包装的condiction不能执行原本自身的dependencyForOperation()方法返回的operation,相应的打开网络,打开定位之类的提示就不会出现,达到了”slient”的效果.
1 | func dependencyForOperation(operation: Operation) -> NSOperation? { |
而NegatedCondition
针对的是Condiction的evaluateForOperation()
方法.它会在condiction不满足的时候调用 completion(.Failed(error)) , 而在满足的时候调用 completion(.Satisfied) , 达到了反转condiction的效果.例如,有一些operation需要网络不成功作为其condiction,就可以把代表网络连接的condiction包装在这个NegatedCondition
中.
1 | func evaluateForOperation(operation: Operation, completion: OperationConditionResult -> Void) { |
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
71override 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 | private func noqueue_addOperation(operation: Operation, category: String) { |
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进行.