Watchpoint (punto de observación) — un mecanismo de depuración que pausa la ejecución del programa cuando cambia el valor de una variable o región de memoria especificada. A diferencia de un breakpoint, que está vinculado a una línea de código, un watchpoint rastrea los cambios de datos independientemente de en qué parte del programa ocurra la modificación. Según la Apple Developer Documentation, los watchpoints son indispensables para depurar condiciones de carrera, cambios inesperados de propiedades y rastrear ciclos de vida de objetos.
Puntos clave
Watchpoint — es un mecanismo del depurador que pausa la ejecución del programa cuando el valor en una dirección de memoria especificada cambia. Si un breakpoint reacciona al alcanzar una línea de código específica, un watchpoint reacciona a la escritura en una celda de memoria específica — independientemente de qué parte del código realizó la escritura. Esto lo hace indispensable para encontrar modificaciones inesperadas de variables, especialmente en aplicaciones multiproceso.
El principio de funcionamiento se basa en el soporte hardware del procesador. Los chips ARM modernos (Apple Silicon, Qualcomm Snapdragon) proporcionan de 4 a 8 registros hardware de watchpoint. Cuando el procesador ejecuta una instrucción de escritura en una dirección que coincide con un watchpoint, se produce una interrupción hardware que el depurador captura. Cuando se agotan los registros hardware, LLDB cambia al modo software — verifica paso a paso cada instrucción de escritura, ralentizando la ejecución decenas de veces.
Según el ARM Architecture Reference Manual, los watchpoints hardware operan a nivel del módulo Data Watchpoint and Trace (DWT) y no requieren modificación del código ejecutable. El tiempo de respuesta es de nanosegundos, lo que permite rastrear incluso cambios de alta frecuencia en bucles de procesamiento de datos como búferes de audio o fotogramas de video.
Watchpoint es indispensable cuando sabes qué variable está cambiando pero no desde dónde. Escenarios típicos: una propiedad frame de UIView cambia sin razón aparente; un contador en un hilo de fondo se reinicia de forma impredecible; un indicador isLoading cambia antes de que se complete una solicitud de red. En cada uno de estos casos, establecer breakpoints en todos los lugares donde podría ocurrir una escritura no es práctico. Un watchpoint resuelve el problema con un solo comando.
Xcode y LLDB admiten tres tipos de watchpoints: watchpoint set variable — para monitorear una variable local; watchpoint set expression — para monitorear una expresión que devuelve una dirección; watchpoint set — para monitorear una dirección de memoria sin procesar. Cada tipo tiene su área de aplicación.
| Tipo de watchpoint | Comando LLDB | Uso |
|---|---|---|
| Variable | watchpoint set variable -w write self.count | Variables locales y globales, propiedades de estructuras |
| Expresión | watchpoint set expression -w write -- &self->mutex.lock | Campos de estructuras por puntero, elementos de arrays por índice |
| Dirección | watchpoint set -w write 0x600000c4b80 | Dirección de memoria específica de la salida anterior de LLDB |
En Xcode, puedes establecer un watchpoint a través del Área de Depuración: detente en un breakpoint, encuentra la variable deseada en el panel Variables View, haz clic derecho y selecciona Watch Variable. Xcode ejecutará automáticamente el comando watchpoint set variable con el nombre de variable y contexto correctos. Después de eso, el depurador se detendrá en cada cambio de valor — conveniente para depuración rápida sin cambiar a la consola. Sin embargo, este método solo funciona mientras la variable esté en el ámbito del frame actual.
func processItems(_ items: [String]) {
var index = 0
// Establecer watchpoint en índice mediante GUI:
// deténgase aquí, clic derecho → Watch Variable
for item in items {
index += 1
print("Elemento \(index): \(item)")
}
}
LLDB proporciona un conjunto completo de comandos para gestionar watchpoints desde la consola. Esto ofrece más control que la GUI: puedes establecer el tamaño de la región de observación, especificar condiciones de activación, crear watchpoints en direcciones obtenidas de expresiones calculadas y ejecutar automáticamente acciones al activarse. La interfaz de comandos es especialmente útil al depurar escenarios complejos que requieren cambiar rápidamente los parámetros de observación.
El comando watchpoint set variable toma un nombre de variable considerando el ámbito: para propiedades self de Objective-C usa self->_property, para Swift usa self.property. El parámetro -w write establece el seguimiento de escritura, -w read — seguimiento de lectura (un modo disponible solo en algunas arquitecturas), -s size — el tamaño de la región en bytes. Después de establecer un watchpoint, puedes ver la lista con el comando watchpoint list.
(lldb) watchpoint set variable -w write -s 8 self.balance
Watchpoint 1: addr = 0x600000c4b80 size = 8 state = enabled type = w
watchpoint spec: 'self.balance'
(lldb) watchpoint set expression -w write -- self->items._storage
Watchpoint 2: addr = 0x600003a4c00 size = 8
(lldb) watchpoint list
1: location = 0x600000c4b80, type = write, variable = '_balance'
2: location = 0x600003a4c00, type = write, expression = 'self->items._storage'
Al igual que los breakpoints, los watchpoints admiten activación condicional. El parámetro -c establece una condición en Swift u Objective-C. Por ejemplo, un watchpoint con la condición newValue > 1000 solo se detendrá cuando el valor escrito supere el millar. Esto es crítico al depurar bucles o sensores que generan miles de cambios por segundo — de lo contrario, el depurador se detendría en cada cambio, imposibilitando el trabajo.
(lldb) watchpoint set variable -w write self.temperature -c "(int)$newValue > 100"
Watchpoint 3: addr = 0x600000e4a20, condition = '(int)$newValue > 100'
(lldb) watchpoint modify 3 -C "po self.temperature" -G true
// Registrar valor automáticamente y continuar la ejecución
Una característica específica de los watchpoints para propiedades de objetos Objective-C y Swift — un watchpoint se establece no en el nombre de la propiedad sino en la dirección del campo ivar (variable de instancia) en la memoria del objeto. Esto significa que con cada nueva asignación de objeto (por ejemplo, al recrear un ViewController), el watchpoint se vuelve inválido porque la dirección de memoria ha cambiado. Para la supervisión persistente de una propiedad entre reinicios, es necesario restablecer el watchpoint en el momento de la inicialización del objeto.
Monitorear elementos de arrays y diccionarios requiere calcular la dirección de un elemento específico. Por ejemplo, para monitorear el tercer elemento de un array usa watchpoint set expression -- &array[2]. Si el array reasigna su búfer interno (al agregar elementos más allá de la capacidad), el watchpoint se vuelve inválido — LLDB reportará un error Watchpoint 1 has an invalid address. En tales casos, debes restablecer el watchpoint después de cambiar el tamaño de la colección.
(lldb) expr var $arr = [10, 20, 30, 40, 50]
(lldb) watchpoint set expression -w write -- &$arr[2]
Watchpoint 4: addr = 0x1000a4b20, size = 8
(lldb) expr $arr[2] = 99
Watchpoint 4 hit: old value: 30, new value: 99
En Objective-C, puedes rastrear el retain count de un objeto estableciendo un watchpoint en el campo retainCount de la estructura objc_object. LLDB lo hace mediante watchpoint set expression -w write -- (int*)[object retainCount]. Sin embargo, para objetos Swift con ARC (Automatic Reference Counting), el retain count no es accesible directamente — en su lugar, usa Instruments o Memory Graph Debugger para el análisis de fugas. Según Apple, los watchpoints en retain count solo funcionan en compilaciones Debug con la optimización ARC deshabilitada.
Los watchpoints tienen varias limitaciones importantes a considerar. La principal — la cantidad de watchpoints hardware está limitada a 4–8 registros en arquitectura ARM y hasta 4 en x86. Cuando todos los registros hardware están ocupados, LLDB cambia al modo de watchpoint software: modifica cada instrucción de escritura en el rango rastreado para generar una excepción. Esto ralentiza la ejecución entre 10 y 50 veces, por lo que en la práctica se recomienda no usar más de 2–3 watchpoints activos simultáneamente.
La segunda limitación — invalidación de watchpoints al reasignar memoria. Cuando se activa la recolección de basura o ARC, y un objeto se mueve en memoria (en lenguajes con compactación del heap), la dirección del watchpoint se vuelve incorrecta. En Swift y Objective-C, ARC no mueve objetos, pero la reasignación de arrays y cadenas causa el mismo efecto. LLDB advierte sobre esto con el mensaje Watchpoint N address (0x...) doesn’t contain a valid allocation.
La tercera limitación — visibilidad de variables. Un watchpoint en una variable local solo funciona mientras esa variable esté dentro del ámbito del frame de pila actual. Una vez que la función termina, el watchpoint se elimina automáticamente. Para monitorear variables globales o campos de objetos de larga duración, un watchpoint persiste hasta que se elimine explícitamente mediante watchpoint delete o hasta que termine el proceso.
(lldb) watchpoint delete 1
1 watchpoints deleted.
(lldb) watchpoint delete # Eliminar todos los watchpoints
2 watchpoints deleted.
(lldb) watchpoint disable 1 # Deshabilitar temporalmente
(lldb) watchpoint enable 1 # Habilitar de nuevo
Según ARM, los watchpoints en Apple Silicon (M1–M4) admiten un tamaño de región monitoreada de 1 a 8 bytes. Para monitorear estructuras de más de 8 bytes, debes establecer múltiples watchpoints en cada campo. Esto es importante al depurar estructuras de datos complejas como CGRect (16 bytes) o UIEdgeInsets (16 bytes).
Preguntas frecuentes
Breakpoint está vinculado a una línea de código — se detiene cuando se alcanza esa línea. Watchpoint está vinculado a una dirección de memoria — se detiene cuando ocurre una escritura en esa dirección desde cualquier parte del programa. Un watchpoint busca “quién está cambiando el valor”, un breakpoint busca “qué sucede en esta línea”.
4–8 watchpoints hardware en ARM (incluyendo Apple Silicon). Superar esto cambia el watchpoint al modo software, ralentizando la ejecución entre 10 y 50 veces. Se recomienda no usar más de 2–3 watchpoints activos a la vez.
Los watchpoints no se conservan entre sesiones de depuración. En cada nuevo inicio, las direcciones de memoria cambian y el watchpoint debe establecerse nuevamente. La excepción son los watchpoints en variables globales con una dirección fija.
Sí, pero el watchpoint se establece en el ivar (almacenamiento subyacente) de la propiedad, no en la propiedad en sí. En Swift, usa watchpoint set variable self.property — LLDB encontrará automáticamente el ivar correspondiente por el nombre de la propiedad.
Los watchpoints hardware no afectan el rendimiento — la interrupción ocurre a nivel del procesador. Los watchpoints software (cuando se agotan los registros hardware) ralentizan la ejecución entre 10 y 50 veces, ya que LLDB verifica cada instrucción de escritura.
Resumen
Desarrollaremos una aplicación móvil llave en mano
IT Sectr crea aplicaciones para iOS y Android para startups y empresas desde 2017. Le asesoraremos y le propondremos la mejor solución.
Lea también