4

我有以下代码在两个属性上添加了一个观察者:

    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

有没有办法做到这一点?

4

1 回答 1

6

首先,请记住 KVO 仅适用于NSObject(或子类)的实例。因此,如果 的origin属性CirclePolygon是 aCGPoint或其他属性,struct当您尝试注册 的观察者时会出现运行时错误\CirclePolygon.origin.x,因为 KVO 无法应用于xa 的属性CGPoint

其次,您给出的关键路径有不同的类型。假设(为了解决我的第一点)您想要观察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>作为方法的keyPath参数observe(_:options:changeHandler:)。不幸的是,该方法声明如下

public func observe<Value>(
        _ keyPath: KeyPath<Self, Value>,
        options: NSKeyValueObservingOptions = [],
        changeHandler: @escaping (Self, NSKeyValueObservedChange<Value>) -> Void)
    -> NSKeyValueObservation

也就是说,该方法需要 aKeyPath<Self, Value>但您传递的是 a 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),
    ]
}
于 2019-01-03T22:56:04.993 回答