【问题标题】:Converting Older KVO to Swift 4将旧版 KVO 转换为 Swift 4
【发布时间】:2018-05-03 16:42:28
【问题描述】:

我正在尝试将一些旧的 WWDC swift 代码转换为 Swift 4。我认为我已经完成了所有工作,除了最后一点会做一些 KVO。这很难缩小到最后一点,因为一切似乎都像示例代码一样运行 - 但这些 KVO 方法在 Swift 4 中没有被调用。我在这里发现了这一点:Open Radar Bug

用 Swift 4 表示以下内容的方式是什么?

// 使用 KVO 机制来指示对“状态”的更改也会影响其他属性

class func keyPathsForValuesAffectingIsReady() -> Set<NSObject> {
    return ["state" as NSObject]
}

class func keyPathsForValuesAffectingIsExecuting() -> Set<NSObject> {
    return ["state" as NSObject]
}

class func keyPathsForValuesAffectingIsFinished() -> Set<NSObject> {
    return ["state" as NSObject]
}

以下是示例中的变量定义:

    override var isReady: Bool {
    switch state {

        case .initialized:
            // If the operation has been cancelled, "isReady" should return true
            return isCancelled

        case .pending:
            // If the operation has been cancelled, "isReady" should return true
            guard !isCancelled else {
                return true
            }

            // If super isReady, conditions can be evaluated
            if super.isReady {
                evaluateConditions()
            }

            // Until conditions have been evaluated, "isReady" returns false
            return false

        case .ready:
            return super.isReady || isCancelled

        default:
            return false
    }
}

override var isExecuting: Bool {
    return state == .executing
}

override var isFinished: Bool {
    return state == .finished
}

如果需要更多代码,请告诉我。

如果这是重复的问题,请在此处链接到重复的问题。我一直找不到解决方案。

【问题讨论】:

    标签: swift swift4 key-value-observing


    【解决方案1】:
    • keyPathsForValuesAffecting… 成员可以是属性而不是方法。
    • 它们必须声明为 @objc,因为 KVO 系统使用 Objective-C 运行时访问属性。
    • 属性的类型应为Set&lt;String&gt;
    • 如果您使用 #keyPath 指令,编译器会在您使用无效键路径时告诉您(例如由于拼写错误或属性名称更改)。

    因此:

    @objc class var keyPathsForValuesAffectingIsReady: Set<String> {
        return [#keyPath(state)]
    }
    
    @objc class var keyPathsForValuesAffectingIsExecuting: Set<String> {
        return [#keyPath(state)]
    }
    
    @objc class var keyPathsForValuesAffectingIsFinished: Set<String> {
        return [#keyPath(state)]
    }
    

    您还需要确保您的 state 属性声明为 @objc dynamic

    【讨论】:

    • 我已经将属性声明为 '''@objc dynamic''',所以对我来说最后的难题是将 '''@objc''' 添加到每个类职能。谢谢!
    • @user3096669 如果您的属性是dynamic,请不要使用这样的裸字符串。请改用#keyPath
    • Apple 提供的相关文档在哪里?请在此处写下链接。提前致谢。
    【解决方案2】:

    主要问题是 KVO 是使用 Objective-C 构建的,它使用 Objective-C 运行时来检测 keyPathsForValuesAffecting 方法的存在。在 Swift 4 中,如果您没有在方法上包含 @objc 注释,则默认情况下方法不再暴露给 Objective-C。因此,简而言之,添加 @objc 注释可能会解决您的问题。

    我做的另一件事——不是绝对必要的,但它使代码看起来更好一点——是将它们声明为静态常量。 @objc 将导致这些作为类方法暴露给Objective-C,所以一切正常,而且稍微干净一些。我也喜欢在它们上面加上private,因为它们永远不会被 Swift 代码调用,而且没有必要弄乱你的类的内部和/或公共接口。

    您还需要确保您的 state 属性符合 KVO,并在更改时发送通知。您可以通过设置属性dynamic 来执行此操作,这将导致KVO 系统为您自动生成通知调用,或者您可以手动调用willChangeValue(for:)didChangeValue(for:)(或基于字符串的版本,@987654332 @ 和 didChangeValue(forKey:)) 在属性的 willSetdidSet 处理程序中。

    最后,如果可以避免的话,不要在 Swift 中使用原始字符串键路径。 #keyPath() 机制是获取基于字符串的键路径的首选方法(对于这些需要获取字符串的传统 Objective-C 方法以外的其他用途,您应该使用新的 KeyPath 类型,这更好)。但是,如果您的 state 属性不是与 Objective-C 兼容的类型,那么您将被旧的字符串键路径所困扰(在这种情况下,您将在 willSetdidSet 中触发通知,如中所述上一段)。或者,您可以创建一个虚拟的 Any 类型对象来镜像您的 state 属性,纯粹用于 KVO 目的。

    所以,是这样的:

    @objc private static let keyPathsForValuesAffectingIsReady: Set<String> = [
        #keyPath(state)
    ]
    

    现在,state 属性。如果是兼容 Objective-C 的类型,那就简单了:

    @objc dynamic var state: ...
    

    或者,如果不是:

    @objc var state: SomeNonObjCThing {
        willSet { self.willChangeValue(forKey: "state") }
        didSet { self.didChangeValue(forKey: "state") }
    }
    

    或者:

    @objc private var _stateKVO: Any { return self.state }
    var state: SomeNonObjCThing {
        willSet { self.willChangeValue(for: \.stateKVO) }
        didSet { self.didChangeValue(for: \.stateKVO) }
    }
    // you can now use #keyPath(_stateKVO) in keyPathsForValuesAffecting...
    

    【讨论】:

    • Apple 或其他地方的相应文档在哪里?因为我找不到任何与 Swift4+ 相关的 KVO。链接等将不胜感激。
    • Apple 的 KVO 文档是 here,但它是基于 Objective-C 的。除此之外,您主要必须依靠理解这两种语言如何交互并填补空白(有一些 Swift 文档here,但它很差)。我写这篇长文的部分原因是为了帮助解决这个问题。
    【解决方案3】:

    (NS)Operation 严重依赖 NSObject-KVO

    最接近的 Swift 语法是

    @objc private class func keyPathsForValuesAffectingIsReady() -> Set<String> {
        return [#keyPath(state)]
    }
    
    @objc private class func keyPathsForValuesAffectingIsExecuting() -> Set<String> {
        return [#keyPath(state)]
    }
    
    @objc private class func keyPathsForValuesAffectingIsFinished() -> Set<String> {
        return [#keyPath(state)]
    }
    

    旁注:您可能需要使state 线程安全。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-01-12
      • 2018-05-18
      • 2015-09-10
      • 2013-03-08
      • 2018-07-31
      • 1970-01-01
      相关资源
      最近更新 更多