将较旧的 KVO 转换为 Swift 4
Converting Older KVO to Swift 4
我正在尝试将一些旧的 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
}
如果需要更多代码,请告诉我。
如果这是一个重复的问题,请link在此处复制。我一直找不到解决方案。
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
。
(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
线程安全。
主要问题是 KVO 是使用 Objective-C 构建的,它使用 Objective-C 运行时来检测 keyPathsForValuesAffecting
方法的存在。在 Swift 4 中,如果您不在方法上包含 @objc
注释,则默认情况下方法不再公开给 Objective-C。因此,简而言之,添加 @objc
注释可能会解决您的问题。
我做的另一件事——不是绝对必要的,但它使代码看起来更好一些——是将它们声明为静态常量。 @objc
将导致这些作为 class 方法暴露给 Objective-C,所以它一切正常,而且稍微干净一些。我也喜欢把 private
放在它们上面,因为它们永远不会被 Swift 代码调用,而且没有必要弄乱你的 class 的内部 and/or public 界面.
您还需要确保您的 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...
我正在尝试将一些旧的 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
}
如果需要更多代码,请告诉我。
如果这是一个重复的问题,请link在此处复制。我一直找不到解决方案。
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
。
(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
线程安全。
主要问题是 KVO 是使用 Objective-C 构建的,它使用 Objective-C 运行时来检测 keyPathsForValuesAffecting
方法的存在。在 Swift 4 中,如果您不在方法上包含 @objc
注释,则默认情况下方法不再公开给 Objective-C。因此,简而言之,添加 @objc
注释可能会解决您的问题。
我做的另一件事——不是绝对必要的,但它使代码看起来更好一些——是将它们声明为静态常量。 @objc
将导致这些作为 class 方法暴露给 Objective-C,所以它一切正常,而且稍微干净一些。我也喜欢把 private
放在它们上面,因为它们永远不会被 Swift 代码调用,而且没有必要弄乱你的 class 的内部 and/or public 界面.
您还需要确保您的 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...