观察 Swift KeyPaths 的列表
Observing a list of Swift KeyPaths
我有以下代码,它在两个属性上添加了一个观察者:
obs = []
let xa = self.observe(\CirclePolygon.radius, options: [.new]) { (op, ch) in
callback()
}
obs.append(xa)
let xb = self.observe(\CirclePolygon.origin.x, options: [.new]) { (op, ch) in
callback()
}
obs.append(xb)
它有效,但我不喜欢重复。我试图传递一个 KeyPaths 列表:
obs = []
let keyPaths = [\CirclePolygon.radius, \CirclePolygon.origin.x]
for keyPath in keyPaths {
let xa = self.observe(keyPath, options: [.new]) { (op, ch) in
callback()
}
obs.append(xa)
}
但是我得到编译器错误:Generic parameter 'Value' could not be inferred
有办法吗?
首先,请记住 KVO 仅适用于 NSObject
(或子类)的实例。因此,如果 CirclePolygon
的 origin
属性 是 CGPoint
或其他 struct
,当您尝试注册 \CirclePolygon.origin.x
的观察者,因为 KVO 无法应用于 CGPoint
.
的 x
属性
其次,你给的关键路径有不同的类型。假设(为了解决我的第一点)你想观察 radius
(a Double
) 和 origin
(a CGPoint
)。关键路径有不同的类型:
\CirclePolygon.radius // type: KeyPath<CirclePolygon, Double>
\CirclePolygon.origin // type: KeyPath<CirclePolygon, CGPoint>
如果您尝试将它们放在一个数组中,而不明确指定数组的类型,Swift 将推导如下:
let keyPaths = [\CirclePolygon.origin, \CirclePolygon.radius]
// deduced type of keyPaths: [PartialKeyPath<CirclePolygon>]
推导出[PartialKeyPath<CirclePolygon>]
是因为PartialKeyPath<CirclePolygon>
是KeyPath<CirclePolygon, Double>
和KeyPath<CirclePolygon, CGPoint>
最近的共同祖先。
因此,在遍历数组的 for
循环中,您将传递静态类型为 PartialKeyPath<CirclePolygon>
的对象作为 observe(_:options:changeHandler:)
的 keyPath
参数] 方法。不幸的是,该方法是 declared as follows:
public func observe<Value>(
_ keyPath: KeyPath<Self, Value>,
options: NSKeyValueObservingOptions = [],
changeHandler: @escaping (Self, NSKeyValueObservedChange<Value>) -> Void)
-> NSKeyValueObservation
也就是说,该方法需要 KeyPath<Self, Value>
但您传递的是 PartialKeyPath<Self>
,因此 Swift 会发出错误。与一般类型问题的 Swift 错误一样,该错误不是很有用。
你不能通过转换来解决这个问题,因为你不能(例如)将 KeyPath<CirclePolygon, Double>
转换为 KeyPath<CirclePolygon, Any>
。
一种解决方案可能是使用本地函数来封装公共代码,如下所示:
func register(callback: @escaping () -> ()) -> [NSKeyValueObservation] {
func r<Value>(_ keyPath: KeyPath<CirclePolygon, Value>) -> NSKeyValueObservation {
return observe(keyPath, options: []) { (_, _) in
callback()
}
}
return [
r(\.radius),
r(\.origin),
]
}
我有以下代码,它在两个属性上添加了一个观察者:
obs = []
let xa = self.observe(\CirclePolygon.radius, options: [.new]) { (op, ch) in
callback()
}
obs.append(xa)
let xb = self.observe(\CirclePolygon.origin.x, options: [.new]) { (op, ch) in
callback()
}
obs.append(xb)
它有效,但我不喜欢重复。我试图传递一个 KeyPaths 列表:
obs = []
let keyPaths = [\CirclePolygon.radius, \CirclePolygon.origin.x]
for keyPath in keyPaths {
let xa = self.observe(keyPath, options: [.new]) { (op, ch) in
callback()
}
obs.append(xa)
}
但是我得到编译器错误:Generic parameter 'Value' could not be inferred
有办法吗?
首先,请记住 KVO 仅适用于 NSObject
(或子类)的实例。因此,如果 CirclePolygon
的 origin
属性 是 CGPoint
或其他 struct
,当您尝试注册 \CirclePolygon.origin.x
的观察者,因为 KVO 无法应用于 CGPoint
.
x
属性
其次,你给的关键路径有不同的类型。假设(为了解决我的第一点)你想观察 radius
(a Double
) 和 origin
(a CGPoint
)。关键路径有不同的类型:
\CirclePolygon.radius // type: KeyPath<CirclePolygon, Double>
\CirclePolygon.origin // type: KeyPath<CirclePolygon, CGPoint>
如果您尝试将它们放在一个数组中,而不明确指定数组的类型,Swift 将推导如下:
let keyPaths = [\CirclePolygon.origin, \CirclePolygon.radius]
// deduced type of keyPaths: [PartialKeyPath<CirclePolygon>]
推导出[PartialKeyPath<CirclePolygon>]
是因为PartialKeyPath<CirclePolygon>
是KeyPath<CirclePolygon, Double>
和KeyPath<CirclePolygon, CGPoint>
最近的共同祖先。
因此,在遍历数组的 for
循环中,您将传递静态类型为 PartialKeyPath<CirclePolygon>
的对象作为 observe(_:options:changeHandler:)
的 keyPath
参数] 方法。不幸的是,该方法是 declared as follows:
public func observe<Value>( _ keyPath: KeyPath<Self, Value>, options: NSKeyValueObservingOptions = [], changeHandler: @escaping (Self, NSKeyValueObservedChange<Value>) -> Void) -> NSKeyValueObservation
也就是说,该方法需要 KeyPath<Self, Value>
但您传递的是 PartialKeyPath<Self>
,因此 Swift 会发出错误。与一般类型问题的 Swift 错误一样,该错误不是很有用。
你不能通过转换来解决这个问题,因为你不能(例如)将 KeyPath<CirclePolygon, Double>
转换为 KeyPath<CirclePolygon, Any>
。
一种解决方案可能是使用本地函数来封装公共代码,如下所示:
func register(callback: @escaping () -> ()) -> [NSKeyValueObservation] {
func r<Value>(_ keyPath: KeyPath<CirclePolygon, Value>) -> NSKeyValueObservation {
return observe(keyPath, options: []) { (_, _) in
callback()
}
}
return [
r(\.radius),
r(\.origin),
]
}