你说:
1.
该stateQueue物业的目的是什么?我看到state计算属性的get和set正在使用它,但是找不到任何解释它们使用的sync:flags:execute和sync:execute方法的文档。
此代码“同步”对属性的访问以使其线程安全。至于为什么你需要做的是,看到的
Operation文件,其中建议:
多核注意事项
…当您继承子类时
NSOperation,必须确保从多个线程中调用的所有重写方法都可以安全使用。如果在子类中实现自定义方法(例如自定义数据访问器),则还必须确保这些方法是线程安全的。因此,必须同步访问操作中的任何数据变量,以防止潜在的数据损坏。有关同步的更多信息,请参见《线程编程指南》。
关于此并发队列用于同步的确切使用,这称为“读取器-写入器”模式。读取器-
写入器模式的基本概念是,读取可以相对于彼此并发发生(因此
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,classfunc keyPathsForValuesAffectingValue(forKey key: String) ->Set<String>但似乎并不能帮助我理解为什么要声明这些方法。
这些是确保变更的只是方法
state的属性的属性触发志愿通知
isReady,
isExecuting以及
isFinished。这三个键的KVO通知对于异步操作的正确运行至关重要。无论如何,《键值观察编程指南:注册从属键》中概述了该语法。
keyPathsForValuesAffectingValue您找到的方法是相关的。您可以使用该方法注册依赖键,也可以使用原始代码段中所示的各个方法。
顺便说一句,这是
AsynchronousOperation您提供的课程的修订版,即:
- 您不得致电
super.start()
。如start
文档所述(强调):
如果要实现并发操作,则必须重写此方法并使用它来启动操作。 您的自定义实现不得
super在任何时候调用。
@objc
在Swift 4中添加必需项。重命名
execute
为usemain
,这是Operation
子类的约定。声明
isReady
为final
属性是不合适的。任何子类都应有权进一步完善其isReady
逻辑(尽管我们承认很少这样做)。使用
#keyPath
,使代码更安全/健壮。使用
dynamic
属性时,您无需手动进行KVO通知。在此示例中,不需要手动调用willChangevalue
和didChangevalue
。进行更改
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 } }}


