Vous pouvez utiliser KVO dans Swift, mais uniquement pour les dynamic
propriétés de la NSObject
sous - classe. Considérez que vous vouliez observer la bar
propriété d'une Foo
classe. Dans Swift 4, spécifiez bar
comme dynamic
propriété dans votre NSObject
sous-classe:
class Foo: NSObject {
@objc dynamic var bar = 0
}
Vous pouvez ensuite vous inscrire pour observer les modifications apportées à la bar
propriété. Dans Swift 4 et Swift 3.2, cela a été considérablement simplifié, comme indiqué dans Utilisation de l'observation des valeurs-clés dans 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)")
}
}
}
Notez que dans Swift 4, nous avons maintenant un typage fort des chemins de clé à l'aide du caractère barre oblique inverse ( \.bar
c'est le chemin de clé pour la bar
propriété de l'objet observé). De plus, comme il utilise le modèle de fermeture de complétion, nous n'avons pas à supprimer manuellement les observateurs (lorsque l'observateur token
tombe hors de portée, l'observateur est supprimé pour nous) ni à nous soucier d'appeler l' super
implémentation si la clé ne le fait pas rencontre. La fermeture est appelée uniquement lorsque cet observateur particulier est appelé. Pour plus d'informations, consultez la vidéo WWDC 2017, Nouveautés de Foundation .
Dans Swift 3, pour observer cela, c'est un peu plus compliqué, mais très similaire à ce que l'on fait en Objective-C. À savoir, vous implémenteriez observeValue(forKeyPath keyPath:, of object:, change:, context:)
ce qui (a) garantit que nous traitons avec notre contexte (et non quelque chose que notre super
instance avait enregistré pour observer); puis (b) soit le manipuler, soit le transmettre à l' super
implémentation, si nécessaire. Et assurez-vous de vous retirer en tant qu'observateur le cas échéant. Par exemple, vous pouvez supprimer l'observateur lorsqu'il est désalloué:
Dans 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])")
}
}
Notez que vous ne pouvez observer que les propriétés qui peuvent être représentées en Objective-C. Ainsi, vous ne pouvez pas observer les génériques, les struct
types Swift, les enum
types Swift , etc.
Pour une discussion sur l'implémentation de Swift 2, voir ma réponse originale ci-dessous.
L'utilisation du dynamic
mot - clé pour atteindre KVO avec des NSObject
sous-classes est décrite dans la section Observation des valeurs-clés du chapitre Adopting Cocoa Design Conventions du guide Utilisation de Swift avec Cocoa et Objective-C :
L'observation des valeurs-clés est un mécanisme qui permet aux objets d'être notifiés des modifications apportées aux propriétés spécifiées d'autres objets. Vous pouvez utiliser l'observation clé-valeur avec une classe Swift, à condition que la classe hérite de la NSObject
classe. Vous pouvez utiliser ces trois étapes pour implémenter l'observation des valeurs-clés dans Swift.
Ajoutez le dynamic
modificateur à toute propriété que vous souhaitez observer. Pour plus d'informations sur dynamic
, voir Exiger une répartition dynamique .
class MyObjectToObserve: NSObject {
dynamic var myDate = NSDate()
func updateDate() {
myDate = NSDate()
}
}
Créez une variable de contexte globale.
private var myContext = 0
Ajoutez un observateur pour le chemin d'accès aux clés, remplacez la observeValueForKeyPath:ofObject:change:context:
méthode et supprimez l'observateur dans 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)
}
}
[Notez que cette discussion KVO a par la suite été supprimée du guide Utilisation de Swift avec Cocoa et Objective-C , qui a été adapté pour Swift 3, mais il fonctionne toujours comme indiqué en haut de cette réponse.]
Il convient de noter que Swift a son propre système d' observateur de propriétés natif , mais c'est pour une classe spécifiant son propre code qui sera exécuté après l'observation de ses propres propriétés. KVO, d'autre part, est conçu pour s'enregistrer pour observer les changements de certaines propriétés dynamiques d'une autre classe.