¿La observación del valor clave(KVO) está disponible en Swift?
key-value-observing (10)
En caso afirmativo, ¿hay alguna diferencia clave que no estuviese presente al usar la observación del valor clave en Objective-C?
¿Otro ejemplo para cualquier persona que se encuentre con un problema con tipos como Int? y CGFloat ?. Simplemente establece su clase como una subclase de NSObject y declara sus variables de la siguiente manera, por ejemplo:
class Theme : NSObject{
dynamic var min_images : Int = 0
dynamic var moreTextSize : CGFloat = 0.0
func myMethod(){
self.setValue(value, forKey: "/(min_images)")
}
}
Actualmente, Swift no admite ningún mecanismo integrado para observar los cambios de propiedad de objetos que no sean ''uno mismo'', por lo que no, no es compatible con KVO.
Sin embargo, KVO es una parte tan fundamental de Objective-C y Cocoa que parece bastante probable que se agregue en el futuro. La documentación actual parece implicar esto:
Observación de valores-clave
Información próximamente.
Además de la respuesta de Rob. Esa clase debe heredar de NSObject
, y tenemos 3 formas de activar el cambio de propiedad
Use setValue(value: AnyObject?, forKey key: String)
de NSKeyValueCoding
class MyObjectToObserve: NSObject {
var myDate = NSDate()
func updateDate() {
setValue(NSDate(), forKey: "myDate")
}
}
Utilice willChangeValueForKey
y didChangeValueForKey
desde NSKeyValueObserving
class MyObjectToObserve: NSObject {
var myDate = NSDate()
func updateDate() {
willChangeValueForKey("myDate")
myDate = NSDate()
didChangeValueForKey("myDate")
}
}
Use dynamic
Ver compatibilidad de tipo Swift
También puede usar el modificador dinámico para exigir que el acceso a los miembros se distribuya dinámicamente a través del tiempo de ejecución de Objective-C si está utilizando API como la observación de clave-valor que reemplaza dinámicamente la implementación de un método.
class MyObjectToObserve: NSObject {
dynamic var myDate = NSDate()
func updateDate() {
myDate = NSDate()
}
}
Y se llama a getter y setter cuando se usa. Puede verificar al trabajar con KVO. Este es un ejemplo de propiedad computada
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
}
}
}
Esto puede ser útil para algunas personas:
// 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
}
}
}
Había usado KVO de esta manera en Swift 3. Puede usar este código con algunos cambios.
Puede usar KVO en Swift, pero solo para dynamic
propiedades dynamic
de la subclase NSObject
. Considere que quería observar la propiedad del bar
de una clase Foo
. En Swift 4, especifique bar
como propiedad dynamic
en su subclase NSObject
:
class Foo: NSObject {
@objc dynamic var bar = 0
}
Luego puede registrarse para observar los cambios en la propiedad de la bar
. En Swift 4 y Swift 3.2, esto se ha simplificado enormemente:
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 fuerte tipado de keypaths usando el carácter de barra diagonal inversa (la barra /.bar
Es la ruta clave para la propiedad de la bar
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 observadores manualmente (cuando el token
cae fuera del alcance, el observador se elimina para nosotros) ni tenemos que preocuparnos de llamar a la super
implementación si la clave no lo hace ''t partido. El cierre se invoca solo cuando se invoca este observador en particular. Para obtener más información, consulte el video WWDC 2017, Novedades en la Fundación .
En Swift 3, para observar esto, es un poco más complicado, pero muy similar a lo que se hace en Objective-C. A saber, implementaría observeValue(forKeyPath keyPath:, of object:, change:, context:)
que (a) se asegura de que estamos tratando con nuestro contexto (y no algo que nuestra super
había 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, tipos de struct
Swift, tipos Swift enum
, etc.
Para una discusión sobre la implementación de Swift 2, vea mi respuesta original, a continuación.
El uso de la palabra clave dynamic
para lograr KVO con subclases NSObject
se describe en la sección Observación de valores clave del capítulo Adoptar convenciones de diseño de cacao de la guía Uso de Swift con cacao y Objective-C :
La observación del valor 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 clase
NSObject
. Puede usar estos tres pasos para implementar la observación de valores-clave en Swift.
Agregue el modificador
dynamic
a cualquier propiedad que desee observar. Para obtener más información sobre ladynamic
, consulte Exigir Despacho 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 clave, y anule el
observeValueForKeyPath:ofObject:change:context:
y elimine al observador endeinit
.
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 KVO se ha eliminado posteriormente de la guía Uso Swift con Cocoa y Objective-C , que se ha adaptado para Swift 3, pero todavía funciona como se indica 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 ejecutará al observar sus propias propiedades. KVO, por otro lado, está diseñado para registrarse para observar cambios en alguna propiedad dinámica de alguna otra clase.
Sí.
KVO requiere despacho dinámico, por lo que simplemente necesita agregar el modificador dynamic
a un método, propiedad, subíndice o inicializador:
dynamic var foo = 0
El modificador dynamic
asegura que las referencias a la declaración se enviarán dinámicamente y se objc_msgSend
través de objc_msgSend
.
Si y no. KVO trabaja en las subclases de NSObject tanto como siempre. No funciona para las clases que no subclasifican NSObject. Swift no tiene (actualmente al menos) tiene su propio sistema de observación nativo.
(Consulte los comentarios sobre cómo exponer otras propiedades como ObjC para que KVO trabaje en ellas)
Consulte la documentación de Apple para ver un ejemplo completo.
Tanto sí como no
Sí , puede usar las mismas antiguas API de KVO en Swift para observar los objetos de Objective-C.
También puede observardynamic
propiedadesdynamic
de objetos Swift que heredan deNSObject
.
Pero ... No , no está fuertemente tipado, como cabría esperar que sea el sistema nativo de observación Swift.
Usando Swift con Cocoa y Objective-C | Observación del valor claveNo , actualmente no existe un sistema de observación de valores incorporados para objetos Swift arbitrarios.
Sí , hay observadores de propiedades integrados, que están fuertemente tipados.
Pero ... No, no son KVO, ya que solo permiten la observación de propiedades propias de los objetos, no admiten observaciones anidadas ("rutas clave"), y usted tiene que implementarlas explícitamente.
El lenguaje de programación Swift | Observadores de propiedadesSí , puede implementar la observación de valores explícitos, que estará fuertemente tipada, y permitirá agregar múltiples manejadores de otros objetos, e incluso admitirá el anidamiento / "rutas clave".
Pero ... No , no será KVO, ya que solo funcionará para propiedades que implemente como observables.
Puede encontrar una biblioteca para implementar dicho valor observando aquí:
Observable-Swift - KVO para Swift - Observación del valor y eventos
Un ejemplo podría ayudar un poco aquí. Si tengo un model
de instancia de clase Model
con atributos, name
y state
, puedo observar esos atributos con:
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)
Los cambios en estas propiedades activarán una llamada a:
override func observeValueForKeyPath(keyPath: String!,
ofObject object: AnyObject!,
change: NSDictionary!,
context: CMutableVoidPointer) {
println("CHANGE OBSERVED: /(change)")
}
Una cosa importante para mencionar es que después de actualizar tu Xcode a 7 beta, es posible que recibas el siguiente mensaje: "El método no anula ningún método de su superclase" . Eso se debe a la opción de los argumentos ''. Asegúrese de que su controlador de observación se ve exactamente como sigue:
override func observeValueForKeyPath(keyPath: String?, ofObject object: AnyObject?, change: [NSObject : AnyObject]?, context: UnsafeMutablePointer<Void>)