Ви можете використовувати 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, Що нового у Фонді .
У Swift 3 спостерігати це дещо складніше, але дуже схоже на те, що робиться в Objective-C. А саме, ви б реалізували, observeValue(forKeyPath keyPath:, of object:, change:, context:)
що (a) гарантує, що ми маємо справу з нашим контекстом (а не з тим, що 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
підкласів описано в розділі « Спостереження за ключовими значеннями» в розділі « Прийняття конвенцій дизайну какао» в посібнику « Використання швидкого використання з какао» та «Ціль-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, з іншого боку, призначений для реєстрації для спостереження за змінами деякої динамічної властивості якогось іншого класу.