Подход Swift 4 для watchValue (forKeyPath:...)

Я пытался найти пример, но то, что я видел, не работает в моем случае.

Что было бы эквивалентно следующему коду:

object.addObserver(self, forKeyPath: "keyPath", options: [.new], context: nil)

override public func observeValue(
    forKeyPath keyPath: String?,
    of object: Any?, 
    change: [NSKeyValueChangeKey : Any]?, 
    context: UnsafeMutableRawPointer?) {

}

Код выше работает, но я получаю предупреждение от SwiftLink:

Предпочитайте новый KVO API на основе блоков с ключевыми дорожками при использовании Swift 3.2 или более поздней версии.

Я ценю это, если вы можете указать мне в правильном направлении.

Ответ 1

Swift 4 представил семейство конкретных типов Key-Path, новое выражение Key-Path Expression для их создания и новую функцию наблюдения за закрытием, доступную для классов, наследующих NSObject.

Используя этот новый набор функций, ваш конкретный пример теперь можно выразить гораздо более кратко:

self.observation = object.observe(\.keyPath) { 
    [unowned self] object, change in
    self.someFunction()
}

Типы вовлеченных

Глоссарий Key-Path

Общая грамматика выражения Key-Path Expression следует за формой \Type.keyPath где Type является конкретным именем типа (включая любые общие параметры), а keyPath - цепочкой из одного или нескольких свойств, индексов или необязательных постфиксаций цепочки/принудительной разворачивания, Кроме того, если keyPath Type можно вывести из контекста, он может быть удален, что приведет к самому \.keyPath.

Все это действительные выражения Key-Path:

\SomeStruct.someValue
\.someClassProperty
\.someInstance.someInnerProperty
\[Int].[1]
\[String].first?.count
\[SomeHashable: [Int]].["aStringLiteral, literally"]!.count.bitWidth

Владение

Вы являетесь владельцем экземпляра NSKeyValueObservation возвращаемого функцией observe, то есть вам больше не нужно addObserver или removeObserver; скорее, вы держите ссылку на нее до тех пор, пока вам понадобятся наблюдения.

Вы также не обязаны делать invalidate(): он будет изящно deinit. Таким образом, вы можете позволить ему жить до тех пор, пока экземпляр, удерживающий его, не умрет, остановите его вручную, не nil ссылку, или даже вызовите invalidate() если вам нужно сохранить ваш экземпляр живым по какой-то вонючей причине.

Предостережения

Как вы могли заметить, наблюдение по-прежнему скрывается в рамках механизма KVO Cocoa, поэтому оно доступно только для классов Obj-C и классов Swift, наследующих NSObject (каждый тип Swift-dev) с добавленным требованием, чтобы любое значение, которое вы намереваетесь наблюдать, должен быть отмечен как @objc (каждый атрибут любимого Swift-dev) и объявлен dynamic.

При этом общий механизм приветствуется, особенно потому, что ему удается Swiftify наблюдать за импортированными NSObjects из модулей, которые могут потребоваться для использования (например, Foundation), и без риска ослабления выразительной способности, с которой мы так много работаем, чтобы получить с каждым нажатием клавиши.

В качестве побочного сведению, Key-Path Expressions Строка по - прежнему необходимы для динамического доступа NSObject свойства КВЦ или вызова value(forKey(Path):)

Вне KVO

Там гораздо больше выражений Key-Path, чем KVO. \Type.path могут быть сохранены как объекты KeyPath для последующего повторного использования. Они выпускаются с возможностью записи, частичного и стираемого стилем. Они могут увеличить выразительную силу функций геттера/сеттера, предназначенных для композиции, не говоря уже о той роли, которую они играют, позволяя тем, у кого самые сильные желудки, вникать в мир функциональных концепций, таких как линзы и призмы. Я предлагаю вам проверить ссылки внизу, чтобы узнать больше о многих дверях разработки, которые они могут открыть.

Ссылки:

Выражение ключевого пути @docs.swift.org

KVO docs @Apple

Предложение Swift Evolution Smart KeyPaths

Ole Begemann Игровая площадка Whats-new-in-Swift-4 с примерами Key-Path

WWDC 2017 Видео: что нового в Foundation 4:35 для SKP и 19:40 для KVO.

Ответ 2

Чтобы добавить что-то в ответ, когда я столкнулся с авариями в своем приложении, используя этот метод в iOS 10.

В iOS 10 вам все равно нужно удалить наблюдателя до освобождения класса, иначе вы получите сообщение об NSInternalInconsistencyException котором говорится:

Экземпляр A класса C был освобожден, а наблюдатели с ключевыми значениями все еще были зарегистрированы с ним.

Чтобы избежать этого сбоя. Просто установите свойство observer, которое вы используете для nil.

deinit {
    self.observation = nil
}