Trying to Understand Asynchronous Operation Subclass(试图理解异步操作子类)
问题描述
我正在尝试在辅助项目中开始使用 Operation
,而不是在我的网络代码中散布基于闭包的回调来帮助消除嵌套调用.所以我在阅读这个主题时,遇到了 这个 实现:
open class AsynchronousOperation: Operation {//MARK: - 属性private let stateQueue = DispatchQueue(label: "asynchronous.operation.state", attributes: .concurrent)私有 var rawState = OperationState.ready私有动态变量状态:OperationState {得到 {返回 stateQueue.sync(执行:{原始状态})}放 {willChangeValue(forKey: "状态")stateQueue.sync(标志:.barrier,执行:{rawState = newValue})didChangeValue(forKey:状态")}}公共最终覆盖 var isReady: Bool {返回状态 == .ready &&super.isReady}公共最终覆盖 var isExecuting: Bool {返回状态 == .executing}公共最终覆盖 var isFinished: Bool {返回状态 == .finished}公共最终覆盖 var isAsynchronous: Bool {返回真}//MARK: - NSObject私有动态类 func keyPathsForValuesAffectingIsReady() ->设置<字符串>{返回[状态"]}私有动态类 func keyPathsForValuesAffectingIsExecuting() ->设置<字符串>{返回[状态"]}私有动态类 func keyPathsForValuesAffectingIsFinished() ->设置<字符串>{返回[状态"]}//MARK: - Foundation.Operation公共最终覆盖 func start() {超级开始()如果是取消{结束()返回}状态 = .executing执行()}//MARK: - 公开///子类必须实现这一点才能执行它们的工作,并且它们不能调用 `super`.此函数的默认实现会引发异常.打开函数执行(){fatalError("子类必须实现`execute`.")}///在任何工作完成后或调用 `cancel()` 后调用此函数以将操作移动到完成状态.公共最终函数完成(){状态 = .finished}}@objc 私有枚举 OperationState: Int {准备好案例案件执行案例完成}
这个Operation
子类有一些实现细节,希望对理解有所帮助.
stateQueue
属性的用途是什么?我看到它被get
和state
计算属性的set
使用,但我找不到任何解释的文档他们使用的sync:flags:execute
和sync:execute
方法.NSObject
部分中返回["state"]
的三个类方法的用途是什么?我没有看到它们在任何地方使用.我发现,在NSObject
,class func keyPathsForValuesAffectingValue(forKey key: String) ->设置<String>
,但这似乎并不能帮助我理解为什么要声明这些方法.
你说:
<块引用>stateQueue
属性的用途是什么?我看到它被state
计算属性的 get 和 set 使用,但我找不到任何解释sync:flags:execute
和sync 的文档:execute
他们使用的方法.
此代码同步";访问属性以使其线程安全.关于为什么需要这样做,请参阅 Operation
文档,建议:
多核注意事项
... 当您继承 NSOperation
时,您必须确保任何被覆盖的方法都可以安全地从多个线程调用.如果您在子类中实现自定义方法,例如自定义数据访问器,您还必须确保这些方法是线程安全的.因此,必须同步访问操作中的任何数据变量,以防止潜在的数据损坏.有关同步的更多信息,请参阅 线程编程指南.
关于同步使用这个并发队列的确切用途,这被称为读写器".图案.读写器模式的这个基本概念是读取可以相对于彼此同时发生(因此 sync
,没有障碍),但写入绝不能相对于任何其他访问同时执行属性(因此 async
带有屏障).
例如,您可以像这样在数组上实现线程安全的读写器:
类 ThreadSafeArray;{私有变量值:[T]private let queue = DispatchQueue(label: "...", attributes: .concurrent)初始化(_值:[T]){self.values = 价值观}func reader<U>(block: () throws -> U) rethrows ->你{返回尝试 queue.sync {尝试块()}}func writer(block: @escaping (inout [T]) -> Void) {queue.async(标志:.barrier){块(&self.values)}}//例如你可以像下面这样使用 `reader` 和 `writer`:下标(_索引:Int)->T{获取 { 读者 { 值 [索引] } }设置 { 作家 { $0[index] = newValue } }}函数追加(_值:T){作家 { $0.append(value) }}func remove(at index: Int) {作家 { $0.remove(at: index)}}}
显然,在这个 Operation
子类中使用 reader-writer 更加简单,但上面说明了这种模式.
你还问过:
<块引用>NSObject
部分中返回[state"]
的三个类方法的用途是什么?我没有看到它们在任何地方使用.我发现,在NSObject
,class func keyPathsForValuesAffectingValue(forKey key: String) ->设置<String>
,但这似乎并不能帮助我理解为什么要声明这些方法.
这些只是确保对 state
属性的更改触发属性的 KVO 通知 isReady
, isExecuting
和 isFinished代码>.这三个键的 KVO 通知对于异步操作的正确运行至关重要.无论如何,这种语法在 Key-Value观察编程指南:注册依赖键.
你找到的keyPathsForValuesAffectingValue
方法是相关的.您可以使用该方法注册相关键,也可以使用原始代码片段中显示的各个方法.
顺便说一句,这是您提供的 AsynchronousOperation
类的修订版,即:
你不能调用
<块引用>super.start()
.正如start
文档 所说(强调添加):如果您要实现并发操作,则必须重写此方法并使用它来启动您的操作.您的自定义实现在任何时候都不得调用
super
.添加 Swift 4 所需的
@objc
.重命名
execute
以使用main
,这是Operation
子类的约定.将
isReady
声明为final
属性是不合适的.任何子类都应该有权进一步完善其isReady
逻辑(尽管我们承认很少这样做).使用
#keyPath
使代码更安全/更健壮.使用
dynamic
属性时,您无需手动进行 KVO 通知.本例不需要手动调用willChangeValue
和didChangeValue
.更改
finish
使其仅在尚未完成时才进入.finished
状态.
因此:
公共类异步操作:操作{///此操作的状态.@objc 私有枚举 OperationState: Int {准备好案例案件执行案例完成}///用于同步访问 `state` 的并发队列.private let stateQueue = DispatchQueue(label: Bundle.main.bundleIdentifier! + ".rw.state", 属性: .concurrent)///`state` 的私有后备存储属性.私有 var _state: OperationState = .ready///操作状态@objc 私有动态变量状态:OperationState {获取 { 返回 stateQueue.sync { _state } }设置 { stateQueue.async(flags: .barrier) { self._state = newValue } }}//MARK: - 各种 `Operation` 属性open override var isReady: Bool { return state == .ready &&超级.isReady }public final override var isExecuting: Bool { return state == .executing }public final override var isFinished: Bool { return state == .finished }公共最终覆盖 var isAsynchronous: Bool { return true }//依赖属性的 KVN打开覆盖类 func keyPathsForValuesAffectingValue(forKey key: String) ->设置<字符串>{if [isReady"、isFinished"、isExecuting"].contains(key) {返回 [#keyPath(state)]}return super.keyPathsForValuesAffectingValue(forKey: key)}//开始公共最终覆盖 func start() {如果是取消{状态 = .finished返回}状态 = .executing主要的()}///子类必须实现这一点才能执行它们的工作,并且它们不能调用 `super`.此函数的默认实现会引发异常.打开覆盖 func main() {fatalError("子类必须实现 `main`.")}///调用此函数完成当前正在执行的操作公共最终函数完成(){如果 !isFinished { 状态 = .finished }}}
I am trying to get started with using Operation
s in a side project rather than having closure-based callbacks littered throughout my networking code to help eliminate nested calls. So I was doing some reading on the subject, and I came across this implementation:
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
}
There are some implementation details of this Operation
subclass that I would like some help in understanding.
What is the purpose of the
stateQueue
property? I see it being used byget
andset
of thestate
computed property, but I can't find any documentation that explains thesync:flags:execute
andsync:execute
methods that they use.What is the purpose of the three class methods in the
NSObject
section that return["state"]
? I don't see them being used anywhere. I found, inNSObject
,class func keyPathsForValuesAffectingValue(forKey key: String) -> Set<String>
, but that doesn't seem to help me understand why these methods are declared.
You said:
- What is the purpose of the
stateQueue
property? I see it being used by get and set of thestate
computed property, but I can't find any documentation that explains thesync:flags:execute
andsync:execute
methods that they use.
This code "synchronizes" access to a property to make it thread safe. Regarding why you need to do that, see the Operation
documentation, which advises:
Multicore Considerations
... When you subclass
NSOperation
, you must make sure that any overridden methods remain safe to call from multiple threads. If you implement custom methods in your subclass, such as custom data accessors, you must also make sure those methods are thread-safe. Thus, access to any data variables in the operation must be synchronized to prevent potential data corruption. For more information about synchronization, see Threading Programming Guide.
Regarding the exact use of this concurrent queue for synchronization, this is known as the "reader-writer" pattern. This basic concept of reader-writer pattern is that reads can happen concurrent with respect to each other (hence sync
, with no barrier), but writes must never be performed concurrently with respect to any other access of that property (hence async
with barrier).
For example, you might implement a reader-writer for thread-safety on an array like so:
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)}
}
}
Obviously, the use of reader-writer in this Operation
subclass is even simpler, but the above illustrates the pattern.
You also asked:
- What is the purpose of the three class methods in the
NSObject
section that return["state"]
? I don't see them being used anywhere. I found, inNSObject
,class func keyPathsForValuesAffectingValue(forKey key: String) -> Set<String>
, but that doesn't seem to help me understand why these methods are declared.
These are just methods that ensure that changes to the state
property trigger KVO notifications for properties isReady
, isExecuting
and isFinished
. The KVO notifications of these three keys is critical for the correct functioning of asynchronous operations. Anyway, this syntax is outlined in the Key-Value Observing Programming Guide: Registering Dependent Keys.
The keyPathsForValuesAffectingValue
method you found is related. You can either register dependent keys using that method, or have the individual methods as shown in your original code snippet.
BTW, here is a revised version of the AsynchronousOperation
class you provided, namely:
You must not call
super.start()
. As thestart
documentation says (emphasis added):If you are implementing a concurrent operation, you must override this method and use it to initiate your operation. Your custom implementation must not call
super
at any time.Add
@objc
required in Swift 4.Renamed
execute
to usemain
, which is the convention forOperation
subclasses.It is inappropriate to declare
isReady
as afinal
property. Any subclass should have the right to further refine itsisReady
logic (though we admittedly rarely do so).Use
#keyPath
to make code a little more safe/robust.You don't need to do manual KVO notifications when using
dynamic
property. The manual calling ofwillChangeValue
anddidChangeValue
is not needed in this example.Change
finish
so that it only moves to.finished
state if not already 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 }
}
}
这篇关于试图理解异步操作子类的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持编程学习网!
本文标题为:试图理解异步操作子类


基础教程推荐
- 如何在 iPhone 上显示来自 API 的 HTML 文本? 2022-01-01
- 在 gmail 中为 ios 应用程序检索朋友的朋友 2022-01-01
- UIWebView 委托方法 shouldStartLoadWithRequest:在 WKWebView 中等效? 2022-01-01
- 如何在 UIImageView 中异步加载图像? 2022-01-01
- Kivy Buildozer 无法构建 apk,命令失败:./distribute.sh -m “kivy"d 2022-01-01
- 当从同一个组件调用时,两个 IBAction 触发的顺序是什么? 2022-01-01
- 如何在没有IB的情况下将2个按钮添加到右侧的UINavigationbar? 2022-01-01
- 如何让对象对 Cocos2D 中的触摸做出反应? 2022-01-01
- Android:对话框关闭而不调用关闭 2022-01-01
- android 应用程序已发布,但在 google play 中找不到 2022-01-01