Operation 的优势
Operation 底层是由 GCD 实现的,他的最大的优点是 复用性
向GCD 中的 queue 中放 task 的方式有两种
- 添加 block(闭包)
- 添加 WorkItem(对象类型)
虽然 workItem 也拥有复用性,但是用户可控性没有 Operation 好
WorkItem 接口
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24 public class DispatchWorkItem {
public init(qos: DispatchQoS = .unspecified, flags: DispatchWorkItemFlags = [], block: @escaping (block) () -> Void)
public func perform()
public func wait()
public func wait(timeout: DispatchTime) -> DispatchTimeoutResult
public func wait(wallTimeout: DispatchWallTime) -> DispatchTimeoutResult
public func notify(qos: DispatchQoS = .unspecified, flags: DispatchWorkItemFlags = [], queue: DispatchQueue, execute: @escaping @convention(block) () -> Void)
// 制定 workItem 的依赖关系
public func notify(queue: DispatchQueue, execute: DispatchWorkItem)
// 取消 work,如果没有开始,那么删除这个 work
// 如果 work 执行中,那么isCancel 会被设置为 true
public func cancel()
public var isCancelled: Bool { get }
}
// 配置 item 的类型
public struct DispatchWorkItemFlags : OptionSet, RawRepresentable {
public static let barrier: DispatchWorkItemFlags
public static let detached: DispatchWorkItemFlags
public static let assignCurrentContext: DispatchWorkItemFlags
public static let noQoS: DispatchWorkItemFlags
public static let inheritQoS: DispatchWorkItemFlags
public static let enforceQoS: DispatchWorkItemFlags
}
1
2
3
4
5
6 let queue = DispatchQueue(label: "xyz")
let backgroundWorkItem = DispatchWorkItem { }
let updateUIWorkItem = DispatchWorkItem { }
backgroundWorkItem.notify(queue: DispatchQueue.main,execute: updateUIWorkItem)
queue.async(execute: backgroundWorkItem)
对于每个 Operation 都有自己的 state
1 | class Operation : NSObject { |
注意:不像 gcd 你要把 gcd block or workItem 放在 queue 里才能执行
Operation 自己可以执行,不过默认情况是 sync 的 eg:
1 | let op = SubOperation() |
如果想要 Operation 异步操作,需要做额外操作,eg 使用OperationQueue
BlockOperation
如果不想使用 OperationQueue 异步并发调度 Operation
可以使用 BlockOperation: Operation,执行在 default global queue.
- 内部管理 concurrent queue(default global queue)
- 内部管理 一个 group
BlockOperation 的效果跟 GCD 中的 Group 相同
不过内部执行是 异步并发的,NSInvocationOperation
在swift 中没有invocation
相关方法在 swift 中都没有
1 | 如果 oc 想要使用 invocation 调用 swift 的方法,那么 swift 中 该方法应该 是 @objc的,响应类也要继承 NSObject |
1 | let sentence = "Ray's courses are the best!" |
OperationQueue
Operation 当你使用 Queue 来管理 Operation 的时候,才能体现他真正的强大的地方,将 Operation 放到 Queue,你不需要自己调用 start,当Queue Scheduler 分配好 thread 给 Operation 的时候,就会 start,operation 的状态由系统决定
1 | open class OperationQueue : NSObject, ProgressReporting { |
在 Operation 中放一个 异步操作是可以的,但是要手动管理 state 变化,因为操作不知道什么时候 finish。
Operation 自己的state 属性又是只读的,所以重写个异步操作自己管理 state
如何创建一个 AsyncOperation
- 自己维护 operation 的state
- 重写属性,isAsynchronous = true
- 注意属性加锁
一个简单的 AsyncOperation 例子
source code
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 open class AsyncOperation: Operation {
enum State: String {
case ready, executing, finished
fileprivate var keyPath: String {
return "is\(rawValue.capitalized)"
}
}
private let rwlockQueue = DispatchQueue(label: "com.czw.asyncoperation", attributes: .concurrent)
private var _state: State = State.ready
var state: State {
get {
rwlockQueue.sync { return _state }
}
set {
willChangeValue(forKey: newValue.keyPath)
rwlockQueue.sync(flags: .barrier) {
_state = newValue
}
didChangeValue(forKey: state.keyPath)
}
}
override open var isReady: Bool {
return super.isReady && state == .ready
}
override open var isExecuting: Bool {
return state == .executing
}
override open var isFinished: Bool {
return state == .finished
}
// When implementing an asynchronous operation object, you must implement this property and return true.
override open var isAsynchronous: Bool {
return true
}
override open func start() {
if isCancelled {
state = .finished
return
}
main()
state = .executing
}
// call this method at main() async block
func finish() {
state = .finished
}
}
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 typealias ImageOperationCompletion = ((Data?, URLResponse?, Error?) -> Void)?
final class NetworkImageOperation: AsyncOperation {
var image: UIImage?
private let url: URL
private let completion: ImageOperationCompletion
init(url: URL, completion: ImageOperationCompletion = nil) {
self.url = url
self.completion = completion
super.init()
}
convenience init?(string: String, completion: ImageOperationCompletion = nil) {
guard let url = URL(string: string) else { return nil }
self.init(url: url, completion: completion)
}
override func main() {
URLSession.shared.dataTask(with: url) { [weak self] data, response, error in
guard let self = self else { return }
defer { self.state = .finished }
if let completion = self.completion {
completion(data, response, error)
return
}
guard error == nil, let data = data else { return }
self.image = UIImage(data: data)
}.resume()
}
}