paint-brush
KVO y KVC en rápidopor@pleelaprasad
42,583 lecturas
42,583 lecturas

KVO y KVC en rápido

Demasiado Largo; Para Leer

El flujo del programa depende del valor de las diversas variables que usamos en nuestro código. Dependiendo de los valores de las variables en nuestro programa, navegamos por el flujo de ejecución según lo necesitemos/requiramos.

Companies Mentioned

Mention Thumbnail
Mention Thumbnail

Coin Mentioned

Mention Thumbnail
featured image - KVO y KVC en rápido
Leela Prasad PENUMUTCHU HackerNoon profile picture
  • Introducción:

El flujo del programa depende del valor de las diversas variables que usamos en nuestro código. Dependiendo de los valores de las variables en nuestro programa, navegamos por el flujo de ejecución según lo necesitemos/requiramos.

Por ejemplo, si usamos cualquier tipo de colección, es difícil realizar la lógica cuando la colección se modifica o cambia. es decir, cuando se agregan, eliminan o modifican nuevos elementos. Todavía podemos manejar este tipo de escenarios de muchas maneras.

Uno de ellos es usar el Centro de notificaciones, para notificarnos cada vez que se produce un cambio en el valor de la propiedad. Pero si desea verificar muchas propiedades en nuestro código, esta forma (usando el Centro de notificaciones) nos lleva a una gran parte del código, para llamar a todas esas propiedades que necesitamos verificar. La otra forma que es mejor en este tipo de escenarios es (también Apple la usa mucho en sus bibliotecas) conocida como KVO (Observación de valor clave), que también está directamente relacionada con otro mecanismo poderoso llamado KVC (Codificación de valor clave).

Nota: cualquier propiedad que deseemos observar para los cambios debe ser una queja de KeyValueCoding (KVC).

Ambos, KVO y KVC, proporcionan una forma eficiente de escribir nuestro código. Ahora comencemos con KVC y luego con KVO.

  • KVC:

KVC es una forma de codificación que le permite acceder indirectamente a las propiedades de un objeto, utilizando cadenas para acceder a ellas en lugar de acceder a los accesores de la propiedad o en lugar de acceder a las variables directamente. Para habilitar dicho mecanismo, sus clases deben cumplir con el protocolo informal NSKeyValueCoding. (O)

La codificación de clave-valor es un mecanismo para acceder indirectamente a las propiedades de un objeto, utilizando cadenas para identificar propiedades, en lugar de invocar un método de acceso o acceder a ellas directamente a través de variables de instancia.

Ej: perfil de clase: NSObject {

var nombre: Cadena

var apellido: Cadena

var customProfile: Perfil

}

Si queremos asignar los valores para las variables declaradas anteriormente, en la clase init() o en algún lugar del archivo de la clase, normalmente hacemos lo siguiente:

self.primerNombre = “Roberto”

self.apellido = "Stark"

Con KVC; nos gusta lo siguiente:

self.setValue: “ Robert ” for key: “ firstName //self.setValue: Any for key: key/KeyPath

self.setValue: “ Stark ” para clave: “ apellido

Para recuperar los valores de las propiedades de KVC, usamos lo siguiente:

let robertLastName = self.value( forKey: " lastName " )

La forma de trabajo de KVC anterior es como trabajar con Diccionarios en Swift. ¿Derecha?

Aquí, en KVC; En lugar de asignar directamente los valores a las propiedades , o usar (#Si está disponible) métodos de establecimiento de objetos , lo estamos haciendo simplemente asignando valores a las claves/keyPaths. Entonces usamos claves y valores , esta técnica se llama Codificación de valores clave (KVC).

Nota: Existe un protocolo conocido como protocolo informal NSKeyValueCoding , que es obligatorio para trabajar con KVC . Nuestras clases deben estar confirmadas con este protocolo para poder usar KVC y KVO . NSObject confirma este protocolo. Por lo tanto , cada clase que se define en el marco de la Fundación y que hereda de NSObject se ajusta al protocolo NSKeyValueCoding .

  • ¿Qué es una clave y KeyPath?

Clave: simplemente " Clave " especifica una sola propiedad, de la que queremos establecer un valor u obtener uno . Por lo tanto, su nombre debe ser el mismo que el nombre de la propiedad.

Ej: self.setValue: " Stark " para clave: " apellido "

KeyPath : KeyPath se forma con la sintaxis de puntos siguiendo las subcadenas, por lo que no es una sola palabra/cadena. Key-path representa todas las propiedades de un objeto, que se interpone en el camino hasta alcanzar el valor/propiedad deseado .

Ex:

var miPerfil: Perfil

self.setValue: “ Baratheon ” for key: “ myProfile. perfil personalizado.apellido

KVC:

  • Aquí, vamos a crear una aplicación de vista única, para trabajar con KVC y KVO. Este proyecto tiene dos proyectos de ejemplo, pero el que estamos explorando aquí no está relacionado con ninguna interfaz de usuario. El otro es actualizar un UILable en la vista de ViewController cuando cambia el texto. El enlace del proyecto se compartirá en la parte inferior, para que pueda explorar fácilmente usted mismo.
  • Primero, cree una aplicación de vista única en Swift
  • Para usar KVC, nuestras clases deben confirmar el protocolo NSKeyValueCoding. Entonces, al confirmar con NSObject, podemos lograr este paso. UIViewController ya está confirmado para este NSObject, por lo que podemos llamar a los métodos de este protocolo sin ninguna configuración.
  • En Xcode, en su proyecto cree un nuevo archivo rápido, con el nombre Niños con NSObject como clase base (Los niños heredan de NSObject). Luego, en ese archivo de clase, declare dos propiedades llamadas ' nombre' y ' edad' colocando las palabras clave ' @objc dynamic ' al principio de la declaración_._ Entonces, ¿por qué necesitamos hacer esto?

→ En swift, hay muchas palabras clave/atributos para ayudar a la compilación, especificaciones de tiempo de ejecución, control de acceso, etc. Ej: @escaping, @disponible, etc. Asimismo, también define un rango de modificadores de declaración, para modificar las declaraciones de propiedades/ miembros de la clase. Por ejemplo, al marcar una declaración de clase con la palabra clave ' final' , le informamos al compilador que la clase no se puede subclasificar. Esto permite que el compilador realice una serie de optimizaciones para aumentar el rendimiento. ' dynamic' también es un modificador de declaración que usamos en swift.

El envío dinámico es una de las características interesantes de Objective-C. Simplemente significa que el tiempo de ejecución de Objective-C decide en tiempo de ejecución qué implementación de un método o función en particular necesita invocar. Por ejemplo, si una subclase anula un método de su superclase, el envío dinámico determina qué implementación del método debe invocarse, la de la subclase o la de la clase principal. Este es un concepto muy poderoso.

Swift usa el tiempo de ejecución de Swift siempre que sea posible. El resultado es que puede realizar una serie de optimizaciones. Mientras que Objective-C se basa únicamente en el envío dinámico, Swift solo opta por el envío dinámico si no tiene otra opción. Si el compilador puede determinar en el momento de la compilación qué implementación de un método debe elegir, gana unos pocos nanosegundos al optar por no participar en el envío dinámico.

El tiempo de ejecución de Swift elige otras opciones, como el envío estático y virtual , sobre el envío dinámico siempre que sea posible. Lo hace para aumentar el rendimiento. El envío estático y virtual es mucho más rápido que el envío dinámico. Aunque estamos hablando de nanosegundos, el resultado neto puede ser espectacular. Muchas funciones a las que nos hemos acostumbrado solo son posibles gracias al tiempo de ejecución dinámico de Objective-C, incluidos los datos básicos y la observación de valores clave.

Envío dinámico

Al aplicar el modificador de declaración ' dynamic' a un miembro de una clase, le dice al compilador que se debe usar el envío dinámico para acceder a ese miembro.

Al anteponer una declaración con la palabra clave ' dinámica' , la declaración se marca implícitamente con el atributo objc . El atributo objc hace que la declaración esté disponible en Objective-C, lo cual es un requisito para que sea enviada por el tiempo de ejecución de Objective-C.

El modificador de declaración ' dinámico' solo se puede usar para miembros de una clase. Las estructuras y las enumeraciones no admiten la herencia, lo que significa que el tiempo de ejecución no tiene que averiguar qué implementación necesita usar.

Entonces, para usar KVC y KVO en Swift, para las propiedades que queremos observar en KVO, debemos declararlas con la palabra clave dinámica @objc.

  • Ahora, tenemos dos propiedades en nuestra clase Children , Inicialízalas/defínelas en el método de inicialización.

  • Ahora, en el archivo de clase ViewController, declare tres instancias secundarias como las siguientes:

Primero usamos el objeto ' child1' . En el método viewDidLoad(), inicializamos el objeto child1 y luego asignamos los valores a sus propiedades.

Si imprime las propiedades de los objetos ' child1' como el nombre y la edad, obtendrá los valores asignados en el resultado.

Ahora, usaremos los métodos KVC para hacer lo mismo.

En el fragmento anterior, en el primer par de filas establecemos los valores deseados para ambas propiedades mediante el método setValue:forKey:. observe que las cadenas de clave son las mismas que los nombres de las propiedades.

A continuación, realizamos exactamente la tarea opuesta. Extraemos los valores de las propiedades usando el método valueForKey: y los asignamos a dos variables locales. Y luego imprimimos los valores en la consola. El resultado es el mismo que el anterior.

Nota: Si damos claves diferentes a los nombres de las propiedades, la aplicación fallará. Al escribir código compatible con KVC, es muy importante tener cuidado de que las cadenas de claves coincidan con los nombres de las propiedades, de lo contrario, la aplicación simplemente se desmoronará. Este no es el caso cuando se trata directamente con propiedades, ya que no hay posibilidad de cometer ningún error en sus nombres; el compilador arrojaría un error en tal caso, lo que nos llevaría a solucionarlo.

Con todo lo anterior, hemos logrado ver cómo escribir código de estilo KVC y cómo establecer y obtener valores usando claves. En la siguiente parte completaremos el proyecto.

trabajando con rutas clave:

Ahora vaya a la clase Niños y agregue la siguiente propiedad.

@objc dynamic var child: ¿Niños?

en el método viewDidLoad . Ahora, agrega las siguientes líneas que inicializan los objetos relacionados y asigna sus valores iniciales:

En el fragmento anterior, en el primer par de filas acabamos de inicializar el objeto '_child2' y su propiedad ' child' . A continuación, establecemos los valores de las propiedades name y age de child2 . Para la propiedad secundaria de child2 , usamos key-path para establecer los valores. Observa de cerca. podemos recuperar los valores para verificar si la asignación fue exitosa: elimine los comentarios de las declaraciones de impresión para eso.

Ahora veremos qué pasa si el hijo del niño también tiene un hijo…

Si desea verificar los valores/resultados, use las declaraciones de impresión para el fragmento anterior.

Hasta ahora, hemos aprendido a escribir el código de quejas de KVC usando claves y rutas de claves. A continuación aprenderemos Observando los cambios de valor de la Propiedad:

KVO :

Aquí veremos qué acciones se deben tomar para poder rastrear los cambios en las propiedades. En primer lugar, permítame presentarle una lista de los pasos necesarios para implementar KVO:

1. La clase cuyas propiedades desea observar debe ser compatible con KVO. Eso significa:

  • La clase debe ser compatible con KVC de acuerdo con lo que ya hemos visto en la introducción y en la sección anterior.
  • La clase debe poder enviar notificaciones de forma automática o manual (veremos más sobre eso más adelante).

2. La clase que se usará para observar la propiedad de otra clase debe establecerse como observador .

3. Un método especial llamado observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) debe implementarse en la clase de observación.

Vamos a ver todo uno por uno. Lo más importante cuando queremos observar los cambios de una propiedad es hacer que nuestra clase observe estos cambios. Esto se hace más o menos como con las notificaciones casuales ( NSNotifications ), pero usando otro método. Este método es addObserver(<#T##observer: NSObject##NSObject#>, forKeyPath: <#T##String#>, options: <#T##NSKeyValueObservingOptions#>, context: <#T##UnsafeMutableRawPointer ?#>)

Aquí observamos los cambios de valor para las propiedades de nombre y edad de un objeto child1 . Entonces, en el método viewWillAppear(), agregue los observadores para el objeto child1.

Los parámetros que acepta el método anterior son:

  • addObserver : esta es la clase de observación, generalmente el objeto propio .
  • forKeyPath : supongo que puedes entender para qué es esto. Es la cadena que usó como clave o ruta clave y coincide con la propiedad que desea observar. Tenga en cuenta que aquí especifica una clave única o una ruta de clave.
  • opciones : una matriz de valores NSKeyValueObservingOptions .
  • contexto : este es un puntero que se puede usar como un identificador único para el cambio de la propiedad que observamos. Por lo general, esto se establece en nil o NULL . Veremos más sobre esto más adelante.

Ahora que hemos hecho que nuestra clase pueda observar cualquier cambio en las dos propiedades anteriores, debemos implementar el método observeValueForKeyPath:ofObject:change:context:. Su implementación es obligatoria y tiene una gran desventaja. Ese es el hecho que se requiere para cada cambio de KVO, y si observa muchas propiedades, entonces debe escribir muchas declaraciones if para tomar las acciones adecuadas para cada propiedad. Sin embargo, esto puede pasarse por alto fácilmente ya que los beneficios del KVO son mayores que esta limitación.

El método anterior se llamará cada vez que cambie el valor de las propiedades que se agregan al observador. Aquí, por parámetro de ruta clave, imprimimos los valores nuevos y antiguos de las propiedades de nombre y edad.

Para probar esta funcionalidad, conecta una acción para un UIButton a tu viewController, y en ese método de acción agrega las siguientes líneas de código:

¡Súper! Después de establecer un nuevo valor para las propiedades de nombre y edad del objeto child1 , recibimos la notificación y los mensajes que solicitamos que se mostraran se mostraron en el depurador. Como ves, tanto el valor anterior como el nuevo están incluidos en el diccionario.

Desde el diccionario de cambios , puede extraer cualquier valor que desee (si es necesario), pero lo más importante de todo es que es muy fácil recibir notificaciones sobre cambios en las propiedades.

agregue las siguientes líneas de código en el método de acción del botón y ejecute el programa.

Si observa el resultado, recibimos dos notificaciones sobre cambios en la propiedad de edad . Pero eso parece confuso, porque aunque conocemos el objeto al que pertenece cada notificación, mediante programación no podemos hacer nada para determinar el objeto que envió la notificación. Entonces, ¿cómo enfrentamos eso y cómo podemos estar 100 % seguros programáticamente sobre el objeto al que pertenece la propiedad modificada?

La respuesta a la pregunta anterior es una: haremos uso del argumento de contexto de addObserver(<#T##observer: NSObject##NSObject#>, forKeyPath: <#T##String#>, options: <# T##NSKeyValueObservingOptions#>, contexto: método <#T##UnsafeMutableRawPointer?#>) . Ya mencioné antes que el propósito del contexto es identificar de manera única un cambio en una propiedad, por lo que es la mejor herramienta que tenemos a nuestra disposición.

Aquí definí dos contextos para cada child1 y child2:

Tenga en cuenta que el valor de contexto para cada propiedad observada debe ser una variable global, porque debe ser accesible desde los métodos addObserver… y observeValueForKeyPath… .

Ahora modifique el código en child1 y child2 agregue métodos de observador, pasando el parámetro de contexto. Y luego necesitamos modificar el método de observación también de la siguiente manera: De esta manera podemos identificar la propiedad modificada con la ayuda del parámetro de contexto pasado. Entonces logramos especificar programáticamente cada propiedad cambiada.

Por último, y antes de llegar al final de este capítulo, también es muy importante eliminar en algún momento los observadores que agregue. No hay una receta sobre dónde debes hacer eso. Por ejemplo, en muchos casos sería útil hacerlo en observeValueForKeyPath:ofObject:change:context: , después de haber manejado una notificación recibida. En otros casos, debe hacerlo al despedir a un controlador de vista. En general, depende de la estructura de su aplicación la decisión que tomará al respecto. En este ejemplo, lo haremos en el método viewWillDissapear:. Aquí está: para child2, eliminé el observador en el fragmento anterior.

Notificaciones automáticas y manuales

De forma predeterminada, el sistema envía una notificación cada vez que se cambia una propiedad cuando observa el uso de KVO. Esto es adecuado en la mayoría de los casos, sin embargo, hay ocasiones en las que no queremos recibir una notificación una vez que se ha producido un cambio, sino después de que se hayan producido varios cambios en varias propiedades o en un momento posterior. Afortunadamente, iOS SDK nos brinda algunos métodos bastante convenientes que nos brindan control sobre las notificaciones, para que podamos enviarlas manualmente cuando sea realmente necesario. Antes de entrar en más detalles, déjame decirte que usar el método que verás a continuación no es obligatorio. Por el contrario, puede implementarlo cuando sea realmente necesario.

Entrando en materia ahora, para controlar las notificaciones que se envían sobre los cambios de propiedad, debe implementar el método _automaticallyNotifiesObserverForKey:_class. El parámetro que acepta es una representación de cadena de la clave de la propiedad para la que necesita controlar la notificación y devuelve un valor booleano. En caso de que no desee que se envíe una notificación después de que se haya cambiado el valor de la propiedad observada, el método debe devolver falso. En cualquier otro caso, debe dejar que iOS decida sobre las notificaciones.

En la práctica, supongamos que no queremos que se publique una notificación cuando se cambie la propiedad de nombre de la clase Children . Con eso en mente, aquí está la implementación de ese método en la clase Children

En la cláusula else , llamamos al mismo método usando la superclase para permitir que iOS maneje todas las claves que no hemos agregado explícitamente aquí, y el valor que obtenemos es el que devolvemos al final.

Si ejecuta la aplicación en este punto, descubrirá que no aparece ningún mensaje sobre el cambio de nombre en el depurador. Por supuesto, esto es lo que deseamos, por lo que hemos logrado lograr nuestro objetivo. Pero, ¿realmente lo hemos hecho?

Bueno, como comprenderá al devolver falso en el método anterior para la clave específica, solo hemos logrado detener el envío de las notificaciones relevantes. Y, por supuesto, esto no significa notificaciones manuales, ¡significa que no hay notificaciones en absoluto! Para enviar una notificación cuando así lo decidamos, debemos utilizar otros dos métodos. Estos son willChangeValueForKey: y didChangeValueForKey: . Al usarlos, primero se debe llamar a willChangeValueForKey:, luego se debe asignar el nuevo valor a la propiedad, mientras que al final se debe llamar a didChangeValueForKey:. Para probar las acciones manuales, agregue estas líneas en el método de acción de viewcontroller.

Si ejecuta la aplicación ahora, el mensaje sobre el cambio de nombre aparece en el depurador, lo que significa que hemos enviado la notificación manualmente con éxito.

En realidad, la notificación se envía después de invocar el método didChangeValueForKey:, por lo tanto, coloque ese método donde considere apropiado para recibir la notificación.

¡Eso es exactamente lo que esperábamos de la aplicación! Como puede ver, hemos logrado controlar el punto de envío de notificaciones, ¡y todo eso con un poco de esfuerzo! Tenga en cuenta que entre los métodos willChangeValueForKey: y didChangeValueForKey:, puede tener más de un valor de propiedad asignado.

Los métodos willChangeValueForKey: y didChangeValueForKey: no son obligatorios para usarse como se muestra arriba. También se pueden implementar en la clase Niños (o en su propia clase).

Haga clic aquí para ver el proyecto que describe el tutorial anterior.

Referencias:

  1. http://michael-brown.net/2017/swift-and-kvo-context-variables/
  2. https://www.appcoda.com/understanding-key-value-observing-coding/
  3. https://cocoacasts.com/key-value-observing-kvo-and-swift-3
  4. https://www.ralfebert.de/ios-examples/swift/property-key-value-observer/
  5. https://blog.scottlogic.com/2015/02/11/swift-kvo-alternativas.html

Si te gustan mis tutoriales, sígueme en medium . Puedes contactarme a través de mi cuenta de twitter o linkedIn .

Gracias por leer. Mejorará pronto con más tutoriales.

¡¡¡Nos vemos!!!