Możesz używać KVO w Swift, ale tylko dla dynamic
właściwości NSObject
podklasy. Weź pod uwagę, że chciałeś obserwować bar
właściwość Foo
klasy. W Swift 4 określ bar
jako dynamic
właściwość w swojej NSObject
podklasie:
class Foo: NSObject {
@objc dynamic var bar = 0
}
Następnie możesz się zarejestrować, aby obserwować zmiany w bar
nieruchomoś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 ( \.bar
jest to keypath dla bar
właściwości obserwowanego obiektu). Ponadto, ponieważ używa on wzorca zamykania zakończenia, nie musimy ręcznie usuwać obserwatorów (gdy token
wypadnie poza zakres, obserwator jest usuwany za nas) ani nie musimy martwić się o wywołanie super
implementacji, 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 super
czego zarejestrowała się nasza instancja); a następnie (b) super
w 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ć struct
typów ogólnych, typów Swift, enum
typów Swift itp.
Omówienie implementacji Swift 2 znajduje się poniżej, w mojej oryginalnej odpowiedzi.
Używanie dynamic
słowa kluczowego w celu osiągnięcia KVO z NSObject
podklasami 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 NSObject
klasie. Możesz użyć tych trzech kroków, aby wdrożyć obserwację klucz-wartość w języku Swift.
Dodaj dynamic
modyfikator 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.