Вы можете использовать KVO в Swift, но только для dynamic
свойств NSObject
подкласса. Учтите, что вы хотели наблюдать за bar
свойством Foo
класса. В Swift 4 укажите bar
как dynamic
свойство в своем NSObject
подклассе:
class Foo: NSObject {
@objc dynamic var bar = 0
}
Затем вы можете зарегистрироваться, чтобы наблюдать за изменениями в bar
собственности. В Swift 4 и Swift 3.2 это было значительно упрощено, как описано в разделе Использование наблюдения значения ключа в Swift :
class MyObject {
private var token: NSKeyValueObservation
var objectToObserve = Foo()
init() {
token = objectToObserve.observe(\.bar) { [weak self] object, change in // the `[weak self]` is to avoid strong reference cycle; obviously, if you don't reference `self` in the closure, then `[weak self]` is not needed
print("bar property is now \(object.bar)")
}
}
}
Обратите внимание, что в Swift 4 теперь мы имеем строгую типизацию путей к ключам с использованием символа обратной косой черты ( \.bar
это путь к ключу для bar
свойства наблюдаемого объекта). Кроме того, поскольку он использует шаблон закрытия завершения, нам не нужно вручную удалять наблюдателей (когда token
выпадет из области видимости, наблюдатель удаляется для нас), а также нам не нужно беспокоиться о вызове super
реализации, если ключ не соответствие. Закрытие вызывается только тогда, когда вызывается этот конкретный наблюдатель. Для получения дополнительной информации см. Видео WWDC 2017, Что нового в Foundation .
В Swift 3 наблюдать это немного сложнее, но очень похоже на то, что делают в Objective-C. А именно, вы бы реализовали, observeValue(forKeyPath keyPath:, of object:, change:, context:)
что (а) гарантирует, что мы имеем дело с нашим контекстом (а не с тем, что наш super
экземпляр зарегистрировал для наблюдения); а затем (b) либо обработать его, либо передать его super
реализации, если необходимо. И обязательно удалите себя в качестве наблюдателя, когда это уместно. Например, вы можете удалить наблюдателя, когда он освобожден:
В Swift 3:
class MyObject: NSObject {
private var observerContext = 0
var objectToObserve = Foo()
override init() {
super.init()
objectToObserve.addObserver(self, forKeyPath: #keyPath(Foo.bar), options: [.new, .old], context: &observerContext)
}
deinit {
objectToObserve.removeObserver(self, forKeyPath: #keyPath(Foo.bar), context: &observerContext)
}
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
guard context == &observerContext else {
super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context)
return
}
// do something upon notification of the observed object
print("\(keyPath): \(change?[.newKey])")
}
}
Обратите внимание, что вы можете наблюдать только те свойства, которые могут быть представлены в Objective-C. Таким образом, вы не можете наблюдать дженерики, struct
типы Swift, enum
типы Swift и т. Д.
Для обсуждения реализации Swift 2, см. Мой оригинальный ответ ниже.
Использование dynamic
ключевого слова для достижения KVO с NSObject
подклассами описано в разделе « Наблюдение значения ключа » главы « Принятие конвенций проектирования какао» руководства « Использование Swift с какао и Objective-C» :
Наблюдение значения ключа - это механизм, который позволяет объектам получать уведомления об изменениях указанных свойств других объектов. Вы можете использовать наблюдение значения ключа с классом Swift, если класс наследует от NSObject
класса. Вы можете использовать эти три шага для реализации наблюдения значения ключа в Swift.
Добавьте dynamic
модификатор к любому свойству, которое вы хотите наблюдать. Для получения дополнительной информации dynamic
см. Раздел Требование динамической отправки .
class MyObjectToObserve: NSObject {
dynamic var myDate = NSDate()
func updateDate() {
myDate = NSDate()
}
}
Создайте глобальную переменную контекста.
private var myContext = 0
Добавьте наблюдателя для ключевого пути, переопределите observeValueForKeyPath:ofObject:change:context:
метод и удалите наблюдателя в deinit
.
class MyObserver: NSObject {
var objectToObserve = MyObjectToObserve()
override init() {
super.init()
objectToObserve.addObserver(self, forKeyPath: "myDate", options: .New, context: &myContext)
}
override func observeValueForKeyPath(keyPath: String?, ofObject object: AnyObject?, change: [String : AnyObject]?, context: UnsafeMutablePointer<Void>) {
if context == &myContext {
if let newValue = change?[NSKeyValueChangeNewKey] {
print("Date changed: \(newValue)")
}
} else {
super.observeValueForKeyPath(keyPath, ofObject: object, change: change, context: context)
}
}
deinit {
objectToObserve.removeObserver(self, forKeyPath: "myDate", context: &myContext)
}
}
[Обратите внимание, что это обсуждение KVO было впоследствии удалено из руководства по использованию Swift с какао и Objective-C , которое было адаптировано для Swift 3, но оно все еще работает, как описано в верхней части этого ответа.]
Стоит отметить , что Swift имеет свою собственную родной наблюдателя свойство системы, но для класса указав свой собственный код , который будет выполняться при наблюдении его собственных свойств. KVO, с другой стороны, предназначен для регистрации, чтобы наблюдать изменения некоторых динамических свойств некоторого другого класса.