Puede usar KVO en Swift, pero solo para dynamic
propiedades de NSObject
subclase. Considere que desea observar la bar
propiedad de una Foo
clase. En Swift 4, especifique bar
como dynamic
propiedad en su NSObject
subclase:
class Foo: NSObject {
@objc dynamic var bar = 0
}
Luego puede registrarse para observar los cambios en la bar
propiedad. En Swift 4 y Swift 3.2, esto se ha simplificado enormemente, como se describe en Uso de la observación de valores clave en 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)")
}
}
}
Tenga en cuenta que en Swift 4, ahora tenemos un tipeo fuerte de las rutas de teclado utilizando el carácter de barra diagonal inversa ( \.bar
es la ruta de acceso de la bar
propiedad del objeto que se está observando). Además, debido a que está utilizando el patrón de cierre de finalización, no tenemos que eliminar manualmente los observadores (cuando la token
caída está fuera del alcance, el observador se elimina por nosotros) ni tenemos que preocuparnos de llamar a la super
implementación si la clave no partido. El cierre se llama solo cuando se invoca a este observador en particular. Para obtener más información, vea el video WWDC 2017, What's New in Foundation .
En Swift 3, para observar esto, es un poco más complicado, pero muy similar a lo que se hace en Objective-C. Es decir, implementaría observeValue(forKeyPath keyPath:, of object:, change:, context:)
qué (a) se asegura de que estemos tratando con nuestro contexto (y no con algo que nuestra super
instancia haya registrado para observar); y luego (b) manejarlo o pasarlo a la super
implementación, según sea necesario. Y asegúrese de retirarse como observador cuando sea apropiado. Por ejemplo, puede eliminar el observador cuando se desasigna:
En 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])")
}
}
Tenga en cuenta que solo puede observar propiedades que se pueden representar en Objective-C. Por lo tanto, no puede observar genéricos, struct
tipos Swift enum
, tipos Swift , etc.
Para una discusión sobre la implementación de Swift 2, vea mi respuesta original, a continuación.
El uso de la dynamic
palabra clave para lograr KVO con NSObject
subclases se describe en la sección Observación de valores clave del capítulo Adopción de convenciones de diseño de cacao de la guía Uso rápido con cacao y Objetivo-C :
La observación de valores clave es un mecanismo que permite que los objetos sean notificados de los cambios en las propiedades especificadas de otros objetos. Puede usar la observación de valores clave con una clase Swift, siempre que la clase herede de la NSObject
clase. Puede utilizar estos tres pasos para implementar la observación de valores clave en Swift.
Agregue el dynamic
modificador a cualquier propiedad que desee observar. Para obtener más información sobre dynamic
, consulte Requerimiento de envío dinámico .
class MyObjectToObserve: NSObject {
dynamic var myDate = NSDate()
func updateDate() {
myDate = NSDate()
}
}
Crea una variable de contexto global.
private var myContext = 0
Agregue un observador para la ruta de acceso clave, anule el observeValueForKeyPath:ofObject:change:context:
método y elimine el observador 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)
}
}
[Tenga en cuenta que esta discusión de KVO se ha eliminado posteriormente de la guía Uso de Swift con Cocoa y Objective-C , que se ha adaptado para Swift 3, pero aún funciona como se describe en la parte superior de esta respuesta.]
Vale la pena señalar que Swift tiene su propio sistema de observación de propiedades nativas , pero eso es para una clase que especifica su propio código que se realizará al observar sus propias propiedades. KVO, por otro lado, está diseñado para registrarse para observar cambios en alguna propiedad dinámica de otra clase.