将较旧的KVO转换为Swift 4

问题描述 投票:1回答:3

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

什么是Swift 4代表以下方式?

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

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
3个回答
3
投票
  • keyPathsForValuesAffecting…成员可以是属性而不是方法。
  • 它们必须声明为@objc,因为KVO系统使用Objective-C运行时访问属性。
  • 属性应该具有类型Set<String>
  • 如果使用#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


1
投票

主要问题是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:)willChangeValue(forKey:)(或基于字符串的版本,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...

0
投票

(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线程安全。

© www.soinside.com 2019 - 2024. All rights reserved.