尝试理解异步操作子类

2024-01-20

我正在尝试开始使用Operation在一个副项目中,而不是在我的网络代码中散布基于闭包的回调,以帮助消除嵌套调用。所以我在读一些关于这个主题的书,然后我发现this https://gist.github.com/calebd/93fa347397cec5f88233执行:

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我想要一些帮助理解的子类。

  1. 目的是什么stateQueue财产?我看到它被使用get and set of the state计算属性,但我找不到任何解释该属性的文档sync:flags:execute and sync:execute他们使用的方法。

  2. 中的三个类方法的目的是什么NSObject返回的部分["state"]?我没有看到它们在任何地方被使用。我发现,在NSObject, class func keyPathsForValuesAffectingValue(forKey key: String) -> Set<String>,但这似乎并不能帮助我理解为什么声明这些方法。


你说:

  1. 目的是什么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子类甚至更简单,但上面说明了该模式。

您还问:

  1. 中的三个类方法的目的是什么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您提供的课程,即:

  1. 你一定不能打电话super.start()。作为start文档 https://developer.apple.com/documentation/foundation/operation/1416837-start说(强调):

    如果要实现并发操作,则必须重写此方法并使用它来启动操作。您的自定义实现不得调用super随时。

  2. Add @objcSwift 4 中需要。

  3. Renamed execute to use main,这是约定Operation子类。

  4. 声明不合适isReady as a final财产。任何子类都应该有权进一步完善其isReady逻辑(尽管我们承认很少这样做)。

  5. Use #keyPath使代码更加安全/健壮。

  6. 使用时不需要手动进行KVO通知dynamic财产。手动调用willChangeValue and didChangeValue本例中不需要。

  7. 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 }
    }
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

尝试理解异步操作子类 的相关文章

随机推荐

  • 如何在多个项目之间共享声明式管道

    我在不同的存储库中有很多项目 它们共享相同的基本 CI 工作流程 我可以轻松地将其表达为声明式管道 pipeline agent any options buildDiscarder logRotator numToKeepStr 20 s
  • 使用相同关键字并条件 Django 查询

    我有一个带有发布模型和标签模型的 Django 应用程序 每份出版物都有一个或多个与其关联的标签 我想使用一组两个标签查询数据库 并且仅返回同时具有这两个标签的出版物 尽管我确信它很容易获得 但我似乎找不到它的语法 我想我没有使用正确的语言
  • 为什么我的应用程序没有绘制任何东西?

    我对 C 一般编程 非常陌生 阅读了 3 周的经验 从 html css 和 javascript 开始 现在开始使用 C 我正在尝试以 Windows 形式制作自己的简单 绘画 应用程序 但我遇到了一个问题 只是无法解决它 无论我阅读或遵
  • 如何在 Javascript 中替换字符串中的撇号?

    给定一个 Javascript 中的字符串 例如 var str this s kelly 我想用另一个字符替换撇号 这是我到目前为止所尝试过的 str replace quot A str replace A 这些都不起作用 我该怎么做
  • 追踪有用信息

    这里聪明的程序员如何跟踪他们在多年的经验中获得的方便的编程技巧和有用的信息 诸如有用的编译器参数 IDE 快捷方式 巧妙的代码片段等 当我查找一两年前我曾经知道的东西时 我有时会感到沮丧 我最喜欢的 IE 可能代表了 20 世纪 90 年代
  • Linq 多个 OR 与列表

    我目前正在开发一个使用 LINQ 的 C 项目 我想知道是否可以使用 Linq 创建一个 多个 OR 子句 并将列表作为 where 的条目 我的意思是这样的 var listofId var sqlQuery from T in myta
  • 加载共享库时出错:?:无法打开共享对象文件:没有这样的文件或目录

    我正在尝试诊断 并修复 位于多个组件边界上的问题 非常感谢任何有关如何获取更多信息或彻底解决问题的见解 我有一个应用程序 它作为 C 程序启动 可以启动 Java 程序 然后可以以递归方式启动相同的 C 程序 它已经在 Linux 和 Cy
  • 了解 WebSocket

    我的理解是 一个socket对应一个网络标识符 端口和TCP标识符 1 操作系统使进程能够与端口关联 IIUC 是使进程在网络上可针对入站数据进行寻址的一种方式 因此 WebSocket 服务器通常会与众所周知的用于接受和理解升级请求的 H
  • Javascript 将字符串匹配到部分匹配

    如何在 Javascript 中进行部分字符串匹配 例如匹配 阿尔夫 阿尔弗雷德 gt 正确 阿尔夫 gt 正确 alf gt 真 al gt 真 改变 gt false 一半 gt 假 gt 假 鲍勃 gt 假 https jsfiddl
  • 将单索引数据帧添加到多索引数据帧、Pandas、Python

    如何将单个数据框添加到多索引数据框 例如 我的多索引数据是 Name Code Buying Date Buying Price Buying Qty Date Code 20140117 none a 1234 20170101 5 7
  • 连接两个 Pandas 数据框

    请给出两个数据框 DF1 A B a1 b1 a2 b2 a3 b3 DF2 C1 C2 C3 0 0 1 我想做以下 DF1 DF2 产生以下结果 A B C1 C2 C3 a1 b1 0 0 1 a2 b2 0 0 1 a3 b3 0
  • 打开Refine - 将另一个文件添加到现有项目中

    我已将 CSV 文件导入到 OR Open Refine 由于我的 CSV 文件包含超过 200 000 条记录 我决定创建单独的文件 因为上传大文件在我的计算机中无法工作 需要很长时间 甚至不确定它是否真正导入 我能够从单个文件 大 中创
  • 相当于 JavaFX8 的 JGraph?

    我想将一个使用旧版本 JGraph 的旧 swing 工具移植到 JavaFX8 然而 由于 JGraph 是一个基于 Swing 的库 因此也考虑替换它 那么 是否有类似于 JGraph 的东西 但与 JavaFX8 一起使用 那么 是否
  • 将类添加到特定类名上的 .hover 功能 - jQuery

    因此 在我的整个文档中 我希望每次用户将鼠标悬停在具有特定类名称的元素上时 我都希望添加一个类 我的 CSS 看起来像这样 hotspot hover hotspothover border 4px solid fff box shadow
  • 查找并重命名所有文件扩展名不正确的图片

    我正在寻找一种方法来自动重命名所有文件扩展名错误的图像 到目前为止 我至少找到了如何获取所有这些文件的列表 find media folder name jpg exec file grep PNG GIF gt foobar txt fi
  • 在scala中玩框架表单验证

    scala 中 play 框架表单验证的工作 跟随我的 Signup 对象 它在 mapping missing 对象表单中方法映射的参数 遵循此方法与 如果你想 将其视为部分应用函数 case class UserRegistration
  • getAllCellInfo 在 android 4.2.1 中返回 null

    我的 Android 版本是 4 2 1 我正在尝试使用TelephonyManager getAllCellInfo 方法 在我的清单文件中我有ACCESS COARSE UPDATES ACCESS COARSE LOCATION AC
  • Jquery Ajax 中的函数作为参数

    是否可以将函数放入 Jquery Ajax 的参数中 如下所示 dataType 和 data 作为函数给出 如果返回类型为 JSON 则 dataType 返回 JSON 值 如果 isJson 为 false 则返回文本 dataVal
  • 裸机 RISC-V CPU - 处理器如何知道从哪个地址开始获取指令?

    我正在设计自己的 RISC V CPU 并且已经能够实现一些指令代码 我已经安装了 RV32I 版本的 GCC 编译器 所以我现在有了汇编器riscv32 unknown elf as可用的 我正在尝试仅用一条指令来汇编一个程序 simpl
  • 尝试理解异步操作子类

    我正在尝试开始使用Operation在一个副项目中 而不是在我的网络代码中散布基于闭包的回调 以帮助消除嵌套调用 所以我在读一些关于这个主题的书 然后我发现this https gist github com calebd 93fa3473