Watchpoint (ponto de observação) — um mecanismo de depuração que pausa a execução do programa quando o valor de uma variável ou região de memória especificada muda. Ao contrário de um breakpoint, que está vinculado a uma linha de código, um watchpoint rastreia alterações de dados independentemente de onde no programa a modificação ocorre. De acordo com a Apple Developer Documentation, os watchpoints são indispensáveis para depurar condições de corrida, alterações inesperadas de propriedades e rastrear ciclos de vida de objetos.
Pontos principais
Watchpoint — é um mecanismo do depurador que pausa a execução do programa quando o valor em um endereço de memória especificado muda. Se um breakpoint reage ao atingir uma linha específica de código, um watchpoint reage à escrita em uma célula de memória específica — independentemente de qual parte do código realizou a escrita. Isso o torna indispensável para encontrar modificações inesperadas de variáveis, especialmente em aplicações multithread.
O princípio de funcionamento é baseado no suporte de hardware do processador. Chips ARM modernos (Apple Silicon, Qualcomm Snapdragon) fornecem de 4 a 8 registradores de watchpoint de hardware. Quando o processador executa uma instrução de escrita em um endereço que corresponde a um watchpoint, ocorre uma interrupção de hardware que o depurador captura. Quando os registradores de hardware se esgotam, o LLDB muda para o modo software — ele verifica passo a passo cada instrução de escrita, retardando a execução dezenas de vezes.
De acordo com o ARM Architecture Reference Manual, os watchpoints de hardware operam no nível do módulo Data Watchpoint and Trace (DWT) e não requerem modificação do código executável. O tempo de resposta é em nanossegundos, permitindo rastrear até alterações de alta frequência em loops de processamento de dados, como buffers de áudio ou quadros de vídeo.
Watchpoint é indispensável quando você sabe qual variável está mudando mas não sabe de onde. Cenários típicos: uma propriedade frame de UIView muda sem motivo aparente; um contador em uma thread de fundo redefine-se imprevisivelmente; uma flag isLoading alterna antes de uma requisição de rede ser concluída. Em cada um desses casos, configurar breakpoints em todos os lugares onde uma escrita poderia ocorrer é impraticável. Um watchpoint resolve o problema com um único comando.
Xcode e LLDB suportam três tipos de watchpoints: watchpoint set variable — para monitorar uma variável local; watchpoint set expression — para monitorar uma expressão que retorna um endereço; watchpoint set — para monitorar um endereço de memória bruto. Cada tipo tem sua área de aplicação.
| Tipo de watchpoint | Comando LLDB | Uso |
|---|---|---|
| Variável | watchpoint set variable -w write self.count | Variáveis locais e globais, propriedades de structs |
| Expressão | watchpoint set expression -w write -- &self->mutex.lock | Campos de structs por ponteiro, elementos de array por índice |
| Endereço | watchpoint set -w write 0x600000c4b80 | Endereço de memória específico da saída anterior do LLDB |
No Xcode, você pode configurar um watchpoint através da Área de Depuração: pare em um breakpoint, encontre a variável desejada no painel Variables View, clique com o botão direito e selecione Watch Variable. O Xcode executará automaticamente o comando watchpoint set variable com o nome e contexto corretos da variável. Depois disso, o depurador pausará a cada alteração de valor — conveniente para depuração rápida sem alternar para o console. No entanto, este método só funciona enquanto a variável estiver no escopo do frame atual.
func processItems(_ items: [String]) {
var index = 0
// Definir watchpoint no índice via GUI:
// pare aqui, clique direito → Watch Variable
for item in items {
index += 1
print("Item \(index): \(item)")
}
}
LLDB fornece um conjunto completo de comandos para gerenciar watchpoints a partir do console. Isso oferece mais controle que a GUI: você pode definir o tamanho da região de observação, especificar condições de disparo, criar watchpoints em endereços obtidos de expressões computadas e executar automaticamente ações ao disparar. A interface de comandos é especialmente útil ao depurar cenários complexos que exigem mudança rápida dos parâmetros de observação.
O comando watchpoint set variable recebe um nome de variável considerando o escopo: para propriedades self do Objective-C use self->_property, para Swift use self.property. O parâmetro -w write define o rastreamento de escrita, -w read — rastreamento de leitura (um modo disponível apenas em algumas arquiteturas), -s size — o tamanho da região em bytes. Após configurar um watchpoint, você pode ver a lista com o 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'
Assim como os breakpoints, os watchpoints suportam disparo condicional. O parâmetro -c define uma condição em Swift ou Objective-C. Por exemplo, um watchpoint com a condição newValue > 1000 só parará quando o valor escrito exceder mil. Isso é crítico ao depurar loops ou sensores que geram milhares de alterações por segundo — caso contrário, o depurador pararia em cada alteração, tornando o trabalho impossível.
(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 automaticamente e continuar a execução
Uma característica específica dos watchpoints para propriedades de objetos Objective-C e Swift — um watchpoint é definido não no nome da propriedade mas no endereço do campo ivar (variável de instância) na memória do objeto. Isso significa que a cada nova alocação de objeto (por exemplo, ao recriar um ViewController), o watchpoint torna-se inválido porque o endereço de memória mudou. Para monitoramento persistente de uma propriedade entre reinicializações, é necessário redefinir o watchpoint no momento da inicialização do objeto.
Monitorar elementos de arrays e dicionários requer calcular o endereço de um elemento específico. Por exemplo, para monitorar o terceiro elemento de um array use watchpoint set expression -- &array[2]. Se o array realocar seu buffer interno (ao adicionar elementos além da capacidade), o watchpoint torna-se inválido — o LLDB reportará um erro Watchpoint 1 has an invalid address. Nesses casos, você precisa redefinir o watchpoint após alterar o tamanho da coleção.
(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
Em Objective-C, você pode rastrear o retain count de um objeto definindo um watchpoint no campo retainCount da estrutura objc_object. O LLDB faz isso através de watchpoint set expression -w write -- (int*)[object retainCount]. No entanto, para objetos Swift com ARC (Automatic Reference Counting), o retain count não é acessível diretamente — em vez disso, use Instruments ou Memory Graph Debugger para análise de vazamentos. De acordo com a Apple, watchpoints em retain count só funcionam em compilações Debug com otimização ARC desabilitada.
Watchpoints têm várias limitações importantes a considerar. A principal — o número de watchpoints de hardware é limitado a 4–8 registradores na arquitetura ARM e até 4 no x86. Quando todos os registradores de hardware estão ocupados, o LLDB muda para o modo de watchpoint software: modifica cada instrução de escrita no intervalo rastreado para gerar uma exceção. Isso retarda a execução em 10–50 vezes, então na prática é recomendado usar não mais que 2–3 watchpoints ativos simultaneamente.
A segunda limitação — invalidação de watchpoints ao realocar memória. Quando a coleta de lixo ou ARC é acionada, e um objeto se move na memória (em linguagens com compactação de heap), o endereço do watchpoint torna-se incorreto. Em Swift e Objective-C, o ARC não move objetos, mas a realocação de arrays e strings causa o mesmo efeito. O LLDB avisa sobre isso com a mensagem Watchpoint N address (0x...) doesn’t contain a valid allocation.
A terceira limitação — visibilidade de variáveis. Um watchpoint em uma variável local só funciona enquanto essa variável estiver dentro do escopo do frame de pilha atual. Assim que a função termina, o watchpoint é removido automaticamente. Para monitorar variáveis globais ou campos de objetos de longa duração, um watchpoint persiste até ser explicitamente excluído via watchpoint delete ou até o processo terminar.
(lldb) watchpoint delete 1
1 watchpoints deleted.
(lldb) watchpoint delete # Excluir todos os watchpoints
2 watchpoints deleted.
(lldb) watchpoint disable 1 # Desabilitar temporariamente
(lldb) watchpoint enable 1 # Habilitar novamente
De acordo com a ARM, watchpoints no Apple Silicon (M1–M4) suportam um tamanho de região monitorada de 1 a 8 bytes. Para monitorar estruturas maiores que 8 bytes, você precisa definir vários watchpoints em cada campo. Isso é importante ao depurar estruturas de dados complexas como CGRect (16 bytes) ou UIEdgeInsets (16 bytes).
Perguntas frequentes
Breakpoint está vinculado a uma linha de código — para quando essa linha é atingida. Watchpoint está vinculado a um endereço de memória — para quando uma escrita ocorre nesse endereço de qualquer lugar do programa. Um watchpoint procura “quem está mudando o valor”, um breakpoint procura “o que acontece nesta linha”.
4–8 watchpoints de hardware no ARM (incluindo Apple Silicon). Exceder isso muda o watchpoint para modo software, retardando a execução em 10–50 vezes. Recomenda-se não usar mais de 2–3 watchpoints ativos por vez.
Watchpoints não são preservados entre sessões de depuração. Em cada nova inicialização, os endereços de memória mudam e o watchpoint deve ser configurado novamente. A exceção são watchpoints em variáveis globais com endereço fixo.
Sim, mas o watchpoint é definido no ivar (armazenamento subjacente) da propriedade, não na propriedade em si. Em Swift, use watchpoint set variable self.property — o LLDB encontrará automaticamente o ivar correspondente pelo nome da propriedade.
Watchpoints de hardware não afetam o desempenho — a interrupção ocorre no nível do processador. Watchpoints de software (quando os registradores de hardware se esgotam) retardam a execução em 10–50 vezes, pois o LLDB verifica cada instrução de escrita.
Resumo
Vamos desenvolver um aplicativo móvel chave na mão
A IT Sectr cria aplicativos para iOS e Android para startups e empresas desde 2017. Nós vamos aconselhá-lo e propor a melhor solução.
Leia também