Możesz używać KVO w Swift, ale tylko dla dynamicwłaściwości NSObjectpodklasy. Weź pod uwagę, że chciałeś obserwować barwłaściwość Fooklasy. W Swift 4 określ barjako dynamicwłaściwość w swojej NSObjectpodklasie:
class Foo: NSObject {
@objc dynamic var bar = 0
}
Następnie możesz się zarejestrować, aby obserwować zmiany w barnieruchomości. W Swift 4 i Swift 3.2 zostało to znacznie uproszczone, jak opisano w Korzystanie z obserwacji wartości kluczowej w języku 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)")
}
}
}
Zauważ, że w Swift 4 mamy teraz silne wpisywanie keypaths przy użyciu znaku ukośnika odwrotnego ( \.barjest to keypath dla barwłaściwości obserwowanego obiektu). Ponadto, ponieważ używa on wzorca zamykania zakończenia, nie musimy ręcznie usuwać obserwatorów (gdy tokenwypadnie poza zakres, obserwator jest usuwany za nas) ani nie musimy martwić się o wywołanie superimplementacji, jeśli klucz nie mecz. Zamknięcie jest wywoływane tylko wtedy, gdy wywoływany jest ten konkretny obserwator. Aby uzyskać więcej informacji, zobacz wideo WWDC 2017, Co nowego w programie Foundation .
Aby to zaobserwować, w Swift 3 jest to nieco bardziej skomplikowane, ale bardzo podobne do tego, co robi się w Objective-C. Mianowicie zaimplementowałbyś to, observeValue(forKeyPath keyPath:, of object:, change:, context:)co (a) upewnia się, że mamy do czynienia z naszym kontekstem (a nie czymś, do superczego zarejestrowała się nasza instancja); a następnie (b) superw razie potrzeby obsłuż go lub przekaż do implementacji. I pamiętaj, aby w razie potrzeby usunąć się jako obserwator. Na przykład możesz usunąć obserwatora, gdy zostanie zwolniony:
W 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])")
}
}
Zauważ, że możesz obserwować tylko właściwości, które mogą być reprezentowane w Objective-C. W związku z tym nie można obserwować structtypów ogólnych, typów Swift, enumtypów Swift itp.
Omówienie implementacji Swift 2 znajduje się poniżej, w mojej oryginalnej odpowiedzi.
Używanie dynamicsłowa kluczowego w celu osiągnięcia KVO z NSObjectpodklasami jest opisane w sekcji Obserwowanie wartości kluczowej rozdziału Adopting Cocoa Design Conventions w przewodniku Using Swift with Cocoa and Objective-C :
Obserwacja klucz-wartość to mechanizm, który umożliwia powiadamianie obiektów o zmianach określonych właściwości innych obiektów. Obserwacji klucz-wartość można używać z klasą Swift, o ile klasa dziedziczy po NSObjectklasie. Możesz użyć tych trzech kroków, aby wdrożyć obserwację klucz-wartość w języku Swift.
Dodaj dynamicmodyfikator do dowolnej właściwości, którą chcesz obserwować. Aby uzyskać więcej informacji dynamic, zobacz Wymaganie dynamicznej wysyłki .
class MyObjectToObserve: NSObject {
dynamic var myDate = NSDate()
func updateDate() {
myDate = NSDate()
}
}
Utwórz globalną zmienną kontekstową.
private var myContext = 0
Dodaj obserwatora dla ścieżki klucza i zastąp observeValueForKeyPath:ofObject:change:context:metodę oraz usuń obserwatora w 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)
}
}
[Uwaga, ta dyskusja na temat KVO została następnie usunięta z przewodnika Używanie języka Swift z kakao i celem-C , który został dostosowany do języka Swift 3, ale nadal działa tak, jak opisano na początku tej odpowiedzi.]
Warto zauważyć, że Swift ma swój własny natywny system obserwacji właściwości , ale dotyczy to klasy określającej własny kod, który zostanie wykonany po obserwacji jej własnych właściwości. Z drugiej strony KVO jest przeznaczony do rejestracji w celu obserwowania zmian pewnej właściwości dynamicznej innej klasy.