Watchpoint (observation point) — a debugging mechanism that pauses program execution when the value of a specified variable or memory region changes. Unlike a breakpoint, which is tied to a line of code, a watchpoint tracks data changes regardless of where in the program the modification occurs. According to Apple Developer Documentation, watchpoints are indispensable for debugging data races, unexpected property changes, and tracking object lifecycles.
Key Takeaways
Watchpoint — is a debugger mechanism that pauses program execution when the value at a specified memory address changes. If a breakpoint reacts to reaching a specific line of code, a watchpoint reacts to writing to a specific memory cell — regardless of which part of the code performed the write. This makes it indispensable for finding unexpected variable modifications, especially in multithreaded applications.
The working principle is based on hardware processor support. Modern ARM chips (Apple Silicon, Qualcomm Snapdragon) provide 4 to 8 hardware watchpoint registers. When the processor executes a write instruction to an address matching a watchpoint, a hardware interrupt occurs, which the debugger catches. When hardware registers run out, LLDB switches to software mode — it step-checks every write instruction, slowing execution by tens of times.
According to the ARM Architecture Reference Manual, hardware watchpoints operate at the Data Watchpoint and Trace (DWT) module level and do not require modification of executable code. The response time is in nanoseconds, allowing tracking even high-frequency variable changes in data processing loops such as audio or video frame buffers.
Watchpoint is indispensable when you know which variable is changing but not where from. Typical scenarios: a UIView frame property changes without apparent reason; a counter in a background thread resets unpredictably; an isLoading flag switches before a network request completes. In each of these cases, setting breakpoints at all places where a write could occur is impractical. A watchpoint solves the problem with a single command.
Xcode and LLDB support three types of watchpoints: watchpoint set variable — for monitoring a local variable; watchpoint set expression — for monitoring an expression that returns an address; watchpoint set — for monitoring a raw memory address. Each type has its own area of application.
| Watchpoint type | LLDB command | Usage |
|---|---|---|
| Variable | watchpoint set variable -w write self.count | Local and global variables, struct properties |
| Expression | watchpoint set expression -w write -- &self->mutex.lock | Struct fields by pointer, array elements by index |
| Address | watchpoint set -w write 0x600000c4b80 | Specific memory address from previous LLDB output |
In Xcode, you can set a watchpoint via the Debug Area: pause at a breakpoint, find the desired variable in the Variables View panel, right-click and select Watch Variable. Xcode will automatically execute the watchpoint set variable command with the correct variable name and context. After that, the debugger will pause on every value change — convenient for quick debugging without switching to the console. However, this method only works while the variable is in scope of the current frame.
func processItems(_ items: [String]) {
var index = 0
// Set watchpoint on index via GUI:
// pause here, right-click → Watch Variable
for item in items {
index += 1
print("Item \(index): \(item)")
}
}
LLDB provides a full set of commands for managing watchpoints from the console. This gives more control than the GUI: you can set the observation region size, specify trigger conditions, create watchpoints on addresses obtained from computed expressions, and automatically execute actions on trigger. The command interface is especially useful when debugging complex scenarios that require quickly changing observation parameters.
The watchpoint set variable command takes a variable name with scope consideration: for Objective-C self-properties use self->_property, for Swift use self.property. The -w write parameter sets write tracking, -w read — read tracking (a mode only available on some architectures), -s size — the region size in bytes. After setting a watchpoint, you can view the list with the watchpoint list command.
(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'
Like breakpoints, watchpoints support conditional triggering. The -c parameter sets a condition in Swift or Objective-C. For example, a watchpoint with the condition newValue > 1000 will only stop when the written value exceeds one thousand. This is critical when debugging loops or sensors generating thousands of changes per second — otherwise the debugger would stop on every change, making work impossible.
(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
// Automatically log value and continue execution
A specific feature of watchpoints for Objective-C and Swift object properties — a watchpoint is set not on the property name but on the address of the ivar (instance variable) field in the object’s memory. This means that with each new object allocation (e.g., when recreating a ViewController), the watchpoint becomes invalid because the memory address has changed. For persistent monitoring of a property across restarts, you need to re-set the watchpoint at the moment of object initialization.
Monitoring elements of arrays and dictionaries requires computing the address of a specific element. For example, to monitor the third element of an array use watchpoint set expression -- &array[2]. If the array reallocates its internal buffer (when adding elements beyond capacity), the watchpoint becomes invalid — LLDB will report an error Watchpoint 1 has an invalid address. In such cases, you need to re-set the watchpoint after changing the collection size.
(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
In Objective-C, you can track an object’s retain count by setting a watchpoint on the retainCount field in the objc_object structure. LLDB does this via watchpoint set expression -w write -- (int*)[object retainCount]. However, for Swift objects with ARC (Automatic Reference Counting), retain count is not directly accessible — instead use Instruments or the Memory Graph Debugger for leak analysis. According to Apple, watchpoints on retain count only work in Debug builds with ARC optimization disabled.
Watchpoints have several important limitations to consider. The main one — the number of hardware watchpoints is limited to 4–8 registers on ARM architecture and up to 4 on x86. When all hardware registers are occupied, LLDB switches to software watchpoint mode: it modifies every write instruction in the tracked range to generate an exception. This slows execution by 10–50 times, so in practice it’s recommended to use no more than 2–3 active watchpoints simultaneously.
The second limitation — invalidation of watchpoints upon memory reallocation. When garbage collection or ARC triggers, and an object moves in memory (in languages with heap compaction), the watchpoint address becomes incorrect. In Swift and Objective-C, ARC does not move objects, but array and string reallocation causes the same effect. LLDB warns about this with the message Watchpoint N address (0x...) doesn’t contain a valid allocation.
The third limitation — variable visibility. A watchpoint on a local variable only works while that variable is within the scope of the current stack frame. Once the function exits, the watchpoint is automatically removed. For monitoring global variables or fields of long-lived objects, a watchpoint persists until explicitly deleted via watchpoint delete or until the process terminates.
(lldb) watchpoint delete 1
1 watchpoints deleted.
(lldb) watchpoint delete # Delete all watchpoints
2 watchpoints deleted.
(lldb) watchpoint disable 1 # Temporarily disable
(lldb) watchpoint enable 1 # Enable again
According to ARM, watchpoints on Apple Silicon (M1–M4) support a monitored region size from 1 to 8 bytes. For monitoring structures larger than 8 bytes, you need to set multiple watchpoints on each field. This is important when debugging complex data structures such as CGRect (16 bytes) or UIEdgeInsets (16 bytes).
Frequently Asked Questions
Breakpoint is tied to a line of code — it stops when that line is reached. Watchpoint is tied to a memory address — it stops when a write occurs to that address from anywhere in the program. A watchpoint looks for “who is changing the value,” a breakpoint looks for “what happens on this line.”
4–8 hardware watchpoints on ARM (including Apple Silicon). Exceeding this switches the watchpoint to software mode, slowing execution by 10–50 times. It is recommended to use no more than 2–3 active watchpoints at a time.
Watchpoints are not preserved between debugging sessions. On each new launch, memory addresses change, and the watchpoint must be set again. The exception is watchpoints on global variables with a fixed address.
Yes, but the watchpoint is set on the ivar (backing storage) of the property, not on the property itself. In Swift, use watchpoint set variable self.property — LLDB will automatically find the corresponding ivar by property name.
Hardware watchpoints do not affect performance — the interrupt happens at the processor level. Software watchpoints (when hardware registers are exhausted) slow execution by 10–50 times, as LLDB checks every write instruction.
Summary
We will develop a mobile application turnkey
IT Sectr creates iOS and Android applications for startups and businesses since 2017. We will advise you and propose the best solution.
Read also