你说:
- 目的是什么
stateQueue
财产?我看到它被 get 和 set 使用state
计算属性,但我找不到任何解释该属性的文档sync:flags:execute
and sync:execute
他们使用的方法。
此代码“同步”对属性的访问以使其线程安全。关于为什么需要这样做,请参阅the Operation文档 https://developer.apple.com/documentation/foundation/operation,建议:
多核注意事项
...当你子类化时NSOperation
,您必须确保任何重写的方法都可以安全地从多个线程调用。如果您在子类中实现自定义方法(例如自定义数据访问器),则还必须确保这些方法是线程安全的。因此,对操作中任何数据变量的访问必须同步,以防止潜在的数据损坏。有关同步的更多信息,请参见线程编程指南 https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/Multithreading/Introduction/Introduction.html#//apple_ref/doc/uid/10000057i.
关于此并发队列进行同步的确切用途,这称为“读取器-写入器”模式。读写器模式的基本概念是读取可以彼此并发发生(因此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
子类甚至更简单,但上面说明了该模式。
您还问:
- 中的三个类方法的目的是什么
NSObject
返回的部分["state"]
?我没有看到它们在任何地方被使用。我发现,在NSObject
, class func keyPathsForValuesAffectingValue(forKey key: String) -> Set<String>
,但这似乎并不能帮助我理解为什么声明这些方法。
这些只是确保更改的方法state
属性触发属性的 KVO 通知isReady https://developer.apple.com/documentation/foundation/operation/1412992-isready, isExecuting https://developer.apple.com/documentation/foundation/operation/1415621-isexecuting and isFinished https://developer.apple.com/documentation/foundation/operation/1413540-isfinished。这三个键的 KVO 通知对于异步操作的正确运行至关重要。无论如何,这个语法在键值观察编程指南:注册相关键 https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/KeyValueObserving/Articles/KVODependentKeys.html.
The keyPathsForValuesAffectingValue
你找到的方法是相关的。您可以使用该方法注册依赖键,也可以使用原始代码片段中所示的各个方法。
顺便说一句,这是一个修订版AsynchronousOperation
您提供的课程,即:
-
你一定不能打电话super.start()
。作为start文档 https://developer.apple.com/documentation/foundation/operation/1416837-start说(强调):
如果要实现并发操作,则必须重写此方法并使用它来启动操作。您的自定义实现不得调用super
随时。
-
Add @objc
Swift 4 中需要。
-
Renamed execute
to use main
,这是约定Operation
子类。
-
声明不合适isReady
as a final
财产。任何子类都应该有权进一步完善其isReady
逻辑(尽管我们承认很少这样做)。
-
Use #keyPath
使代码更加安全/健壮。
-
使用时不需要手动进行KVO通知dynamic
财产。手动调用willChangeValue
and didChangeValue
本例中不需要。
-
Change finish
这样它只会移动到.finished
如果尚未完成,请说明。
Thus:
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 }
}
}