Наблюдение за списком Swift KeyPaths - PullRequest
0 голосов
/ 03 января 2019

У меня есть следующий код, который добавляет наблюдателя на два свойства:

    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

Есть ли способ сделать это?

1 Ответ

0 голосов
/ 04 января 2019

Прежде всего, помните, что KVO работает только на экземплярах NSObject (или подклассе).Поэтому, если origin свойство CirclePolygon является CGPoint или каким-либо другим struct, вы получите ошибку во время выполнения при попытке зарегистрировать наблюдателя на \CirclePolygon.origin.x, поскольку KVO не может применяться кx свойство 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

То есть метод требует 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),
    ]
}
...