KVO (Key-Value Observing) parece sencillo. Al registrarlo con addObserver, recibes una notificación cada vez que cambia la propiedad.
En el artículo relacionado Método Swizzling en Objective-C: guía completa encontrarás los conceptos de base y casos de aplicación relacionados.
Pero ¿no resulta extraño? Aunque no añadieras código de notificación al setter, ¿cómo sabe el runtime que cambió el valor?
La respuesta es contundente. Al añadir un observador, el runtime sustituye silenciosamente la clase del objeto.
La técnica se llama isa-swizzling. Si el method swizzling del artículo anterior cambiaba la «fila» de una tabla de conexiones, aquí se reemplaza la «clase» a la que pertenece el objeto.
Qué ocurre al ejecutar addObserver
Person Al añadir un observador, el runtime hace lo siguiente.
- Crea dinámicamente la subclase
NSKVONotifying_Person**** - Sobrescribe el setter **** de la propiedad observada, con una versión que inserta notificaciones antes y después del cambio.
- Sustituye el puntero isa **** del objeto por la nueva subclase.
Después, al ejecutar person.name = @"Kim", el flujo de llamadas es el siguiente.
// NSKVONotifying_Person pseudocódigo del setter sobrescrito
- (void)setName:(NSString *)name {
[self willChangeValueForKey:@"name"];
[super setName:name]; // ejecución del setter original
[self didChangeValueForKey:@"name"]; // notificar al observador
}
El contenido de memoria no cambia; solo cambia la clase a la que pertenece, así que el código existente no nota nada. Al eliminarse el último observador, isa vuelve a la clase original.
La clase miente: class vs object_getClass
Aquí aparece un detalle interesante. Al preguntar la clase de un objeto observado, hay dos respuestas posibles.
[person class]; // Person — mentira
object_getClass(person); // NSKVONotifying_Person — verdad
El runtime también sobrescribe -class en la subclase dinámica para que **** responda la clase original. Así oculta los detalles de implementación y evita confundir al desarrollador.
object_getClass() lee directamente el puntero isa, por lo que muestra la clase real. Si los valores difieren durante la depuración, el objeto está siendo observado.
Sin pasar por el setter, no hay notificación
Al entender isa-swizzling, la conocida limitación de KVO resulta lógica.
person.name = @"Kim"; // notificación — mediante el setter sobrescrito
person->_name = @"Kim"; // sin notificación — ivar modificación directa, setter sin pasar por él
El origen de la notificación KVO es el setter sobrescrito. Modificar directamente el ivar evita esa ruta y no ocurre nada. Es una razón práctica para recomendar la sintaxis de punto al acceder a propiedades en Objective-C, pues llama al setter internamente.
Si debes cambiar el valor sin usar el setter, puedes envolverlo en una notificación manual.
[self willChangeValueForKey:@"name"];
_name = @"Kim";
[self didChangeValueForKey:@"name"];
Para desactivar las notificaciones automáticas de una propiedad, devuelve NO desde automaticallyNotifiesObserversForKey:.
Precauciones prácticas
La gestión del ciclo de vida de la observación sigue siendo importante. Debes conservar el token NSKeyValueObservation devuelto por la API basada en bloques mientras necesites observar (Documentación de KVO de Apple).
Al invalidar o liberar el token al finalizar la observación, también se elimina el registro. Si mantienes la API basada en cadenas, debes coordinar manualmente el ciclo de vida de addObserver y removeObserver (Documentación de KVO de Apple).
Mezclar KVO y method swizzling en una misma clase causa problemas de orden. Si KVO se añade después del swizzling, la subclase dinámica envuelve el setter modificado; en el orden inverso, sus supuestos pueden no coincidir.
Cuando dos técnicas que manipulan el runtime se encuentran en un objeto, la depuración se vuelve mucho más difícil.
En Swift, @objc dynamic es obligatorio.
class Person: NSObject {
@objc dynamic var name: String = ""
}
let observation = person.observe(\.name, options: [.new]) { _, change in
print(change.newValue ?? "")
}
Esto se debe a que isa-swizzling requiere que la llamada al setter pase por la ruta de mensajes de Objective-C.
Desde Swift 4 se usa la API basada en bloques observe(_:options:changeHandler:). Al liberarse el token NSKeyValueObservation devuelto, también se elimina automáticamente el registro. Using Key-Value Observing in Swift
Así se evitan la mayoría de los problemas de la era de los keyPath basados en cadenas.
Resumen
- Al registrar un observador, KVO crea dinámicamente la subclase **NSKVONotifying_ y sustituye isa **
- El origen de la notificación es el setter ** sobrescrito **, con willChange/didChange insertados.
-classestá manipulado para responder la clase original; la real se ve conobject_getClass().- Modificar directamente el ivar evita el setter y no genera notificaciones; si hace falta, notifica manualmente con willChange/didChange.
- En Swift,
@objc dynamicy la API observe basada en bloques son el estándar; liberar el token elimina el registro.
Al incluir method swizzling e isa-swizzling, se entiende el runtime de Objective-C: tanto la tabla de métodos como la clase del objeto son datos que pueden cambiar en runtime. Esa flexibilidad origina gran parte de la magia de Cocoa.
Seguir leyendo
- Método Swizzling en Objective-C: guía completa
- +load vs +initialize: momento de llamada y trampas de la herencia
- Categorías de Objective-C vs extensiones de Swift: diferencias, conceptos y precauciones
Fuentes y verificación
- Using Key-Value Observing in SwiftApple · Documentación oficial · Consultado 17 de agosto de 2026Respalda: Registro de KVO en Swift, notificaciones de cambios y ciclo de vida del token de observación

