我正在尝试Operation在辅助项目中使用s,而不是在整个网络代码中乱扔基于闭包的回调,以帮助消除嵌套调用。因此,我在阅读有关该主题的文章时,遇到了此实现:
Operation
open class AsynchronousOperation: Operation { // MARK: - Properties private let stateQueue = DispatchQueue(label: "asynchronous.operation.state", attributes: .concurrent) private var rawState = OperationState.ready private dynamic var state: OperationState { get { return stateQueue.sync(execute: { rawState }) } set { willChangeValue(forKey: "state") stateQueue.sync(flags: .barrier, execute: { rawState = newValue }) didChangeValue(forKey: "state") } } public final 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 } public final override var isAsynchronous: Bool { return true } // MARK: - NSObject private dynamic class func keyPathsForValuesAffectingIsReady() -> Set<String> { return ["state"] } private dynamic class func keyPathsForValuesAffectingIsExecuting() -> Set<String> { return ["state"] } private dynamic class func keyPathsForValuesAffectingIsFinished() -> Set<String> { return ["state"] } // MARK: - Foundation.Operation public final override func start() { super.start() if isCancelled { finish() return } state = .executing execute() } // MARK: - Public /// Subclasses must implement this to perform their work and they must not call `super`. The default implementation of this function throws an exception. open func execute() { fatalError("Subclasses must implement `execute`.") } /// Call this function after any work is done or after a call to `cancel()` to move the operation into a completed state. public final func finish() { state = .finished } } @objc private enum OperationState: Int { case ready case executing case finished }
Operation我希望对该子类有一些实现细节,以帮助他们理解。
该stateQueue物业的目的是什么?我看到它是由计算属性使用get和set的state,但是我找不到任何说明它们使用的sync:flags:execute和sync:execute方法的文档。
stateQueue
get
set
state
sync:flags:execute
sync:execute
NSObject返回部分中的三个类方法的目的是什么["state"]?我看不到它们在任何地方都被使用过。我在中找到了NSObject,class func keyPathsForValuesAffectingValue(forKey key: String) -> Set<String>但似乎并不能帮助我理解为什么要声明这些方法。
NSObject
["state"]
class func keyPathsForValuesAffectingValue(forKey key: String) -> Set<String>
你说:
1. 该stateQueue物业的目的是什么?我看到state计算属性的get和set正在使用它,但是找不到任何解释它们使用的sync:flags:execute和sync:execute方法的文档。
此代码“同步”对属性的访问以使其线程安全。至于为什么你需要做的是,看到的Operation文件,其中建议:
多核注意事项 …当您继承子类时NSOperation,必须确保从多个线程中调用的所有重写方法都可以安全使用。如果在子类中实现自定义方法(例如自定义数据访问器),则还必须确保这些方法是线程安全的。因此,必须同步访问操作中的任何数据变量,以防止潜在的数据损坏。有关同步的更多信息,请参见《线程编程指南》。
多核注意事项
…当您继承子类时NSOperation,必须确保从多个线程中调用的所有重写方法都可以安全使用。如果在子类中实现自定义方法(例如自定义数据访问器),则还必须确保这些方法是线程安全的。因此,必须同步访问操作中的任何数据变量,以防止潜在的数据损坏。有关同步的更多信息,请参见《线程编程指南》。
NSOperation
关于此并发队列用于同步的确切使用,这称为“读取器-写入器”模式。读取器- 写入器模式的基本概念是,读取可以相对于彼此并发发生(因此sync,没有障碍),但是绝对不能相对于该属性的任何其他访问同时进行写入(因此async有障碍)。
sync
async
例如,您可以像这样在数组上实现用于线程安全的读写器:
class ThreadSafeArray<T> { private var values: [T] private let queue = DispatchQueue(label: "...", attributes: .concurrent) init(_ values: [T]) { self.values = values } func reader<U>(block: () throws -> U) rethrows -> U { return try queue.sync { try block() } } func writer(block: @escaping (inout [T]) -> Void) { queue.async(flags: .barrier) { block(&self.values) } } // e.g. you might use `reader` and `writer` like the following: subscript(_ index: Int) -> T { get { reader { values[index] } } set { writer { $0[index] = newValue } } } func append(_ value: T) { writer { $0.append(value) } } func remove(at index: Int) { writer { $0.remove(at: index)} } }
显然,在此Operation子类中使用reader-writer 更加简单,但是上面说明了这种模式。
您还问:
2. NSObject返回部分中的三个类方法的目的是什么["state"]?我看不到它们在任何地方都被使用过。我在中找到了NSObject,class func keyPathsForValuesAffectingValue(forKey key: String) -> Set<String>但似乎并不能帮助我理解为什么要声明这些方法。
这些是确保变更的只是方法state的属性的属性触发志愿通知isReady,isExecuting以及isFinished。这三个键的KVO通知对于异步操作的正确运行至关重要。无论如何,《键值观察编程指南:注册从属键》中概述了该语法。
isReady
isExecuting
isFinished
keyPathsForValuesAffectingValue您找到的方法是相关的。您可以使用该方法注册依赖键,也可以使用原始代码段中所示的各个方法。
keyPathsForValuesAffectingValue
顺便说一句,这是AsynchronousOperation您提供的课程的修订版,即:
AsynchronousOperation
super.start()
start
如果要实现并发操作,则必须重写此方法并使用它来启动操作。 您的自定义实现不得super在任何时候调用。
super
@objc在Swift 4中添加必需项。
@objc
重命名execute为use main,这是Operation子类的约定。
execute
main
声明isReady为final属性是不合适的。任何子类都应有权进一步完善其isReady逻辑(尽管我们承认很少这样做)。
final
使用#keyPath,使代码更安全/健壮。
#keyPath
使用dynamic属性时,您无需手动进行KVO通知。在此示例中,不需要手动调用willChangeValue和didChangeValue。
dynamic
willChangeValue
didChangeValue
进行更改finish,使其仅在.finished尚未完成时才进入状态。
finish
.finished
从而:
public class AsynchronousOperation: Operation { /// State for this operation. @objc private enum OperationState: Int { case ready case executing case finished } /// Concurrent queue for synchronizing access to `state`. private let stateQueue = DispatchQueue(label: Bundle.main.bundleIdentifier! + ".rw.state", attributes: .concurrent) /// Private backing stored property for `state`. private var _state: OperationState = .ready /// The state of the operation @objc private dynamic var state: OperationState { get { return stateQueue.sync { _state } } set { stateQueue.async(flags: .barrier) { self._state = newValue } } } // MARK: - Various `Operation` properties 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 } public final override var isAsynchronous: Bool { return true } // 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 { state = .finished 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 !isFinished { state = .finished } } }