[iOS] Asynchronous Operation
iOS的Operation是synchronous的, 無法做到一些asynchronous的動作如排序下載檔案, 這時候就需要自己寫一個asynchronous的Operation.
FCAsynchronousOperation:
(1.) 繼承Operation
(2.) override isAsynchronous變數, 設為true
(3.) override isReady, isExecuting, isFinished 三個變數
(此例用一個state變數結合KVO來實現)
(4.) override start()與main() function
(5.) 新增一個finish() function 來定義Asynchronous Operation何時結束
(6.) 繼承FCAsynchronousOperation的subclass 需implement main() function
import UIKit
public class FCAsynchronousOperation: Operation {
@objc private enum State: Int {
case ready
case executing
case finished
}
/// Concurrent queue for synchronizing access to `state`.
private let stateQueue = DispatchQueue(label: Bundle.main.bundleIdentifier! + ".operationQueue.state", attributes: .concurrent)
/// Private backing stored property for `state`.
private var _state: State = .ready
/// The state of the operation
@objc private dynamic var state: State {
get { return stateQueue.sync { _state } }
set { stateQueue.sync(flags: .barrier) { _state = newValue } }
}
// MARK: - Various `Operation` properties
open override var isAsynchronous: Bool { return true }
open override var isReady: Bool { return state == .ready && super.isReady }
public final override var isExecuting: Bool { return state == .executing }
public final override var isFinished: Bool { return state == .finished }
// KVN for dependent properties
open override class func keyPathsForValuesAffectingValue(forKey key: String) -> Set<String> {
if ["isReady", "isFinished", "isExecuting"].contains(key) {
return [#keyPath(state)]
}
return super.keyPathsForValuesAffectingValue(forKey: key)
}
// Start
public final override func start() {
if isCancelled {
finish()
return
}
state = .executing
main()
}
/// Subclasses must implement this to perform their work and they must not call `super`. The default implementation of this function throws an exception.
open override func main() {
fatalError("Subclasses must implement `main`.")
}
/// Call this function to finish an operation that is currently executing
public final func finish() {
if isExecuting { state = .finished }
}
}
Last updated