Si tel est le cas, y a-t-il des différences clés qui n'étaient pas autrement présentes lors de l'utilisation de l'observation valeur-clé dans Objective-C?
Oui et non. KVO fonctionne sur les sous-classes NSObject comme il l’a toujours fait. Cela ne fonctionne pas pour les classes qui ne sous-classent pas NSObject. Swift ne possède pas (actuellement au moins) son propre système d'observation natif.
(Voir les commentaires pour savoir comment exposer d'autres propriétés comme ObjC afin que KVO fonctionne dessus)
Voir le Documentation Apple pour un exemple complet.
Vous pouvez utiliser KVO dans Swift, mais uniquement pour les propriétés dynamic
de la sous-classe NSObject
. Considérez que vous vouliez observer la propriété bar
d'une classe Foo
. Dans Swift 4, spécifiez bar
comme propriété dynamic
dans votre sous-classe NSObject
:
class Foo: NSObject {
@objc dynamic var bar = 0
}
Vous pouvez ensuite vous inscrire pour observer les modifications apportées à la propriété bar
. Dans Swift 4 et Swift 3.2, cela a été grandement simplifié, comme indiqué dans tilisation de l'observation de la valeur clé 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 une forte saisie des raccourcis clavier utilisant le caractère barre oblique inversée (le \.bar
est le raccourci clavier de la propriété bar
de l'objet observé). De plus, comme il utilise le modèle de fin d'exécution, nous n'avons pas besoin de supprimer manuellement les observateurs (lorsque la token
tombe en dehors de la portée, l'observateur est supprimé pour nous) et nous n'avons pas à nous soucier d'appeler le super
mise en œuvre si la clé ne correspond pas. La fermeture est appelée uniquement lorsque cet observateur particulier est appelé. Pour plus d'informations, voir la vidéo WWDC 2017, Nouveautés de Foundation .
Dans Swift 3, observer ceci est un peu plus compliqué, mais très similaire à ce que l'on fait en Objective-C. En d'autres termes, vous implémenteriez observeValue(forKeyPath keyPath:, of object:, change:, context:)
qui (a) garantit que nous traitons notre contexte (et non quelque chose que notre instance super
s'était enregistrée pour observer); et ensuite (b) soit le gérer, soit le transmettre à la mise en oeuvre super
, 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 pouvant être représentées dans Objective-C. Ainsi, vous ne pouvez pas observer les génériques, les types Swift struct
, les types Swift enum
, etc.
Pour une discussion sur l'implémentation Swift 2, voir ma réponse initiale, ci-dessous.
L'utilisation du mot clé dynamic
pour obtenir le KVO avec les sous-classes NSObject
est décrite dans la section Observation de la valeur-clé de la section Adoption de conventions de conception de cacao chapitre 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 informés des modifications apportées aux propriétés spécifiées d'autres objets. Vous pouvez utiliser l'observation valeur-clé avec une classe Swift, tant que la classe hérite de la classe
NSObject
. Vous pouvez utiliser ces trois étapes pour implémenter l'observation valeur-clé dans Swift.
Ajoutez le modificateur
dynamic
à toute propriété à observer. Pour plus d'informations surdynamic
, reportez-vous à la section Requérir une répartition dynamique .class MyObjectToObserve: NSObject { dynamic var myDate = NSDate() func updateDate() { myDate = NSDate() } }
Créez une variable de contexte global.
private var myContext = 0
Ajoutez un observateur pour le chemin d'accès clé, substituez la méthode
observeValueForKeyPath:ofObject:change:context:
et supprimez l'observateur dansdeinit
.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) } }
[Remarque: cette discussion KVO a par la suite été retirée du guide Utilisation de Swift avec Cocoa et Objective-C , qui a été adapté pour Swift 3, mais cela fonctionne toujours comme indiqué en haut de cette réponse.]
Il est à noter que Swift a son propre système observateur de propriété , mais c'est pour une classe spécifiant son propre code qui sera exécuté lors de l'observation de ses propres propriétés. KVO, en revanche, est conçu pour s'inscrire afin d'observer les modifications apportées à une propriété dynamique d'une autre classe.
Oui et non:
Oui , vous pouvez utiliser les mêmes anciennes API KVO dans Swift pour observer les objets Objective-C.
Vous pouvez également observer les propriétés dynamic
des objets Swift héritant de NSObject
.
Mais ... Non ce n’est pas très typé, comme on pouvait s’y attendre Swift comme système d’observation natif.
tilisation de Swift avec Cocoa et Objective-C | Key Value Observing
Non , il n'existe actuellement aucun système d'observation des valeurs intégrées pour les objets Swift arbitraires.
Oui , il y a des éléments intégrés Property Observers, qui sont fortement typés.
Mais ... Non ils ne sont pas du KVO, car ils ne permettent que d'observer les propriétés propres des objets, ne prennent pas en charge les observations imbriquées. ("chemins clés"), et vous devez les implémenter explicitement.
Le Swift langage de programmation | Observateurs de propriétés
Oui , vous pouvez implémenter l'observation de valeur explicite, qui sera fortement typée, et permettre l'ajout de plusieurs gestionnaires à partir d'autres objets, et même prendre en charge l'emboîtement/"clé chemins ".
Mais ... Non il ne s'agira pas de KVO car il ne fonctionnera que pour les propriétés que vous implémenterez en tant qu'observables.
Vous pouvez trouver une bibliothèque pour implémenter une telle valeur en observant ici:
Observable-Swift - KVO pour Swift - Observation de la valeur et événements
Un exemple pourrait aider un peu ici. Si j'ai une instance model
de la classe Model
avec les attributs name
et state
, je peux observer ces attributs avec:
let options = NSKeyValueObservingOptions([.New, .Old, .Initial, .Prior])
model.addObserver(self, forKeyPath: "name", options: options, context: nil)
model.addObserver(self, forKeyPath: "state", options: options, context: nil)
Les modifications apportées à ces propriétés déclencheront un appel à:
override func observeValueForKeyPath(keyPath: String!,
ofObject object: AnyObject!,
change: NSDictionary!,
context: CMutableVoidPointer) {
println("CHANGE OBSERVED: \(change)")
}
Oui.
KVO requiert une répartition dynamique, il vous suffit donc d'ajouter le modificateur dynamic
à une méthode, une propriété, un indice ou un initialiseur:
dynamic var foo = 0
Le modificateur dynamic
garantit que les références à la déclaration seront distribuées de manière dynamique et accessibles via objc_msgSend
.
En plus de la réponse de Rob. Cette classe doit hériter de NSObject
, et nous avons 3 façons de déclencher un changement de propriété
Utilisez setValue(value: AnyObject?, forKey key: String)
à partir de NSKeyValueCoding
class MyObjectToObserve: NSObject {
var myDate = NSDate()
func updateDate() {
setValue(NSDate(), forKey: "myDate")
}
}
Utilisez willChangeValueForKey
et didChangeValueForKey
à partir de NSKeyValueObserving
class MyObjectToObserve: NSObject {
var myDate = NSDate()
func updateDate() {
willChangeValueForKey("myDate")
myDate = NSDate()
didChangeValueForKey("myDate")
}
}
Utilisez dynamic
. Voir Compatibilité Swift Type
Vous pouvez également utiliser le modificateur dynamique pour exiger que l'accès aux membres soit réparti de manière dynamique via le runtime d'Objective-C si vous utilisez des API, telles que l'observation clé-valeur, qui remplacent dynamiquement l'implémentation d'une méthode.
class MyObjectToObserve: NSObject {
dynamic var myDate = NSDate()
func updateDate() {
myDate = NSDate()
}
}
Et la propriété getter et setter est appelée lorsqu'elle est utilisée. Vous pouvez vérifier lorsque vous travaillez avec KVO. Ceci est un exemple de propriété calculée
class MyObjectToObserve: NSObject {
var backing: NSDate = NSDate()
dynamic var myDate: NSDate {
set {
print("setter is called")
backing = newValue
}
get {
print("getter is called")
return backing
}
}
}
Actuellement, Swift ne prend en charge aucun mécanisme intégré permettant d’observer les modifications de propriétés d’objets autres que 'self'. Par conséquent, non, il ne prend pas en charge KVO.
Cependant, le KVO est un élément tellement fondamental d'Objective-C et de cacao qu'il semble tout à fait probable qu'il soit ajouté à l'avenir. La documentation actuelle semble impliquer ceci:
Observation de la valeur clé
Informations à venir.
Une chose importante à mentionner est que, après la mise à jour de votre Xcode à 7 bêta, le message suivant peut s'afficher: "La méthode ne remplace aucune méthode de sa super-classe ". C'est à cause du caractère optionnel des arguments. Assurez-vous que votre gestionnaire d'observation ressemble exactement à ce qui suit:
override func observeValueForKeyPath(keyPath: String?, ofObject object: AnyObject?, change: [NSObject : AnyObject]?, context: UnsafeMutablePointer<Void>)
Cela peut être utile à quelques personnes -
// MARK: - KVO
var observedPaths: [String] = []
func observeKVO(keyPath: String) {
observedPaths.append(keyPath)
addObserver(self, forKeyPath: keyPath, options: [.old, .new], context: nil)
}
func unObserveKVO(keyPath: String) {
if let index = observedPaths.index(of: keyPath) {
observedPaths.remove(at: index)
}
removeObserver(self, forKeyPath: keyPath)
}
func unObserveAllKVO() {
for keyPath in observedPaths {
removeObserver(self, forKeyPath: keyPath)
}
}
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
if let keyPath = keyPath {
switch keyPath {
case #keyPath(camera.iso):
slider.value = camera.iso
default:
break
}
}
}
J'avais utilisé KVO de cette manière dans Swift 3. Vous pouvez utiliser ce code avec peu de modifications.
Un autre exemple pour quiconque rencontre un problème avec des types tels que Int? et CGFloat ?. Vous définissez simplement votre classe en tant que sous-classe de NSObject et déclarez vos variables comme suit, par exemple:
class Theme : NSObject{
dynamic var min_images : Int = 0
dynamic var moreTextSize : CGFloat = 0.0
func myMethod(){
self.setValue(value, forKey: "\(min_images)")
}
}