我正在尝试将一些旧的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
}
如果需要更多代码,请告诉我。
如果这是一个重复的问题,请在此处链接到副本。我一直无法找到解决方案。
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
。
主要问题是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:)
和willSet
) didSet
为该物业提供处理。
最后,如果可以避免,请不要在Swift中使用原始字符串键路径。 #keyPath()
机制是获取基于字符串的密钥路径的首选方法(对于除了需要采用字符串的这些传统Objective-C方法之外的其他用途,您应该使用更好的新KeyPath
类型)。但是,如果你的state
属性不是Objective-C兼容类型,那么你就会遇到旧的字符串键路径(在这种情况下,你将按照前一段所述在你的willSet
和didSet
中触发通知)。或者,您可以创建一个虚拟的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...
(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
线程安全。