Puede usar KVO en Swift, pero solo para dynamicpropiedades de NSObjectsubclase. Considere que desea observar la barpropiedad de una Fooclase. En Swift 4, especifique barcomo dynamicpropiedad en su NSObjectsubclase:
class Foo: NSObject {
@objc dynamic var bar = 0
}
Luego puede registrarse para observar los cambios en la barpropiedad. 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 ( \.bares la ruta de acceso de la barpropiedad 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 tokencaída está fuera del alcance, el observador se elimina por nosotros) ni tenemos que preocuparnos de llamar a la superimplementació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 superinstancia haya registrado para observar); y luego (b) manejarlo o pasarlo a la superimplementació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, structtipos 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 dynamicpalabra clave para lograr KVO con NSObjectsubclases 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 NSObjectclase. Puede utilizar estos tres pasos para implementar la observación de valores clave en Swift.
Agregue el dynamicmodificador 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.