Puoi usare KVO in Swift, ma solo per le dynamic
proprietà della NSObject
sottoclasse. Considera che volevi osservare la bar
proprietà di una Foo
classe. In Swift 4, specifica bar
come dynamic
proprietà nella NSObject
sottoclasse:
class Foo: NSObject {
@objc dynamic var bar = 0
}
È quindi possibile registrarsi per osservare le modifiche alla bar
proprietà. In Swift 4 e Swift 3.2, questo è stato notevolmente semplificato, come indicato in Uso dell'osservazione dei valori-chiave in 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)")
}
}
}
Nota, in Swift 4, ora abbiamo una forte tipizzazione di keypath usando il carattere barra rovesciata ( \.bar
è il keypath per la bar
proprietà dell'oggetto che si sta osservando). Inoltre, poiché utilizza il modello di chiusura del completamento, non è necessario rimuovere manualmente gli osservatori (quando il token
campo di applicazione non rientra, l'osservatore viene rimosso per noi) né dobbiamo preoccuparci di chiamare l' super
implementazione se la chiave non lo fa incontro. La chiusura viene chiamata solo quando viene invocato questo particolare osservatore. Per ulteriori informazioni, vedere il video del WWDC 2017, Novità nella Fondazione .
In Swift 3, osservare questo, è un po 'più complicato, ma molto simile a quello che si fa in Objective-C. Vale a dire, implementeresti observeValue(forKeyPath keyPath:, of object:, change:, context:)
quale (a) assicurati che abbiamo a che fare con il nostro contesto (e non qualcosa che la nostra super
istanza si era registrata per osservare); e poi (b) gestirlo o passarlo super
all'implementazione, se necessario. E assicurati di rimuoverti come osservatore quando appropriato. Ad esempio, è possibile rimuovere l'osservatore quando viene deallocato:
In 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])")
}
}
Nota, puoi solo osservare le proprietà che possono essere rappresentate in Objective-C. Pertanto, non è possibile osservare generici, struct
tipi Swift enum
, tipi Swift , ecc.
Per una discussione sull'implementazione di Swift 2, vedi la mia risposta originale, di seguito.
L'uso della dynamic
parola chiave per ottenere KVO con le NSObject
sottoclassi è descritto nella sezione Osservazione dei valori-chiave del capitolo Adozione di convenzioni di progettazione del cacao della guida Uso di Swift con cacao e Objective-C :
L'osservazione del valore-chiave è un meccanismo che consente agli oggetti di ricevere notifiche delle modifiche alle proprietà specificate di altri oggetti. Puoi utilizzare l'osservazione del valore-chiave con una classe Swift, purché la classe erediti dalla NSObject
classe. Puoi utilizzare questi tre passaggi per implementare l'osservazione del valore-chiave in Swift.
Aggiungi il dynamic
modificatore a qualsiasi proprietà che desideri osservare. Per ulteriori informazioni su dynamic
, vedere Richiesta di invio dinamico .
class MyObjectToObserve: NSObject {
dynamic var myDate = NSDate()
func updateDate() {
myDate = NSDate()
}
}
Crea una variabile di contesto globale.
private var myContext = 0
Aggiungi un osservatore per il percorso chiave, sovrascrivi il observeValueForKeyPath:ofObject:change:context:
metodo e rimuovi l'osservatore 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)
}
}
[Nota, questa discussione KVO è stata successivamente rimossa dalla guida Uso di Swift con Cocoa e Objective-C , che è stata adattata per Swift 3, ma funziona ancora come indicato all'inizio di questa risposta.]
Vale la pena notare che Swift ha il proprio sistema di osservazione delle proprietà native , ma questo è per una classe che specifica il proprio codice che verrà eseguito all'osservazione delle proprie proprietà. KVO, d'altra parte, è progettato per registrarsi per osservare le modifiche ad alcune proprietà dinamiche di altre classi.