KVO (Key-Value Observing) parece simples. Ao registrar com addObserver, você recebe uma notificação sempre que a propriedade muda.
No artigo relacionado Method Swizzling em Objective-C: guia completo, você encontra os conceitos básicos e casos de aplicação relacionados.
Mas não é estranho? Sem adicionar código de notificação ao setter, como o runtime sabe que o valor mudou?
A resposta é direta. Ao adicionar um observador, o runtime troca silenciosamente a classe do objeto.
Essa técnica se chama isa-swizzling. Se o method swizzling do artigo anterior trocava a “linha” de uma tabela de conexões, aqui a própria “classe” do objeto é substituída.
O que acontece ao executar addObserver
Person Ao adicionar um observador, o runtime faz o seguinte.
- Cria dinamicamente a subclasse
NSKVONotifying_Person**** - Sobrescreve o setter **** da propriedade observada, usando uma versão que insere notificações antes e depois da alteração.
- Troca o ponteiro isa **** do objeto pela nova subclasse.
Depois, ao executar person.name = @"Kim", o fluxo de chamadas fica assim.
// NSKVONotifying_Person pseudocódigo do setter sobrescrito
- (void)setName:(NSString *)name {
[self willChangeValueForKey:@"name"];
[super setName:name]; // execução do setter original
[self didChangeValueForKey:@"name"]; // notificar o observador
}
O conteúdo da memória permanece igual; apenas a classe muda, então o código existente não percebe nada. Quando o último observador é removido, isa volta à classe original.
A classe mente: class vs object_getClass
Aqui surge um detalhe interessante. Ao perguntar a classe de um objeto observado, há duas respostas possíveis.
[person class]; // Person — mentira
object_getClass(person); // NSKVONotifying_Person — verdade
O runtime também sobrescreve -class na subclasse dinâmica para que **** responda a classe original. Assim oculta detalhes de implementação e evita confundir o desenvolvedor.
object_getClass() lê diretamente o ponteiro isa, exibindo a classe real. Se os valores forem diferentes durante a depuração, o objeto está sendo observado.
Sem passar pelo setter, não há notificação
Ao entender o princípio do isa-swizzling, a famosa limitação do KVO fica evidente.
person.name = @"Kim"; // notificação — via o setter sobrescrito
person->_name = @"Kim"; // sem notificação — ivar alteração direta, setter sem passar por ele
A origem da notificação do KVO é o setter sobrescrito. Alterar o ivar diretamente ignora esse caminho, então nada acontece. Esse é um dos motivos práticos para recomendar a sintaxe de ponto no acesso a propriedades em Objective-C, que chama o setter internamente.
Se você precisar alterar o valor sem usar o setter, envolva a operação em uma notificação manual.
[self willChangeValueForKey:@"name"];
_name = @"Kim";
[self didChangeValueForKey:@"name"];
Para desativar as notificações automáticas de uma propriedade, retorne NO em automaticallyNotifiesObserversForKey:.
Cuidados práticos
Gerenciar o ciclo de vida da observação continua sendo importante. Mantenha o token NSKeyValueObservation retornado pela API baseada em blocos enquanto a observação for necessária (Documentação de KVO da Apple).
Ao invalidar ou liberar o token ao terminar a observação, o registro também é removido. Se você mantiver a API baseada em strings, deverá sincronizar manualmente o ciclo de vida de addObserver e removeObserver (Documentação de KVO da Apple).
Misturar KVO e method swizzling na mesma classe causa problemas de ordem. Se o KVO for adicionado depois do swizzling, a subclasse dinâmica envolve o setter alterado; na ordem inversa, as suposições podem não coincidir.
Quando duas técnicas que manipulam o runtime se encontram em um objeto, a depuração fica muito mais difícil.
No Swift, @objc dynamic é obrigatório.
class Person: NSObject {
@objc dynamic var name: String = ""
}
let observation = person.observe(\.name, options: [.new]) { _, change in
print(change.newValue ?? "")
}
Isso acontece porque o isa-swizzling exige que a chamada do setter passe pelo caminho de mensagens do Objective-C.
A partir do Swift 4, usa-se a API baseada em blocos observe(_:options:changeHandler:). Quando o token NSKeyValueObservation retornado é liberado, o registro também é removido automaticamente. Using Key-Value Observing in Swift
Assim, a maior parte dos problemas da era dos keyPath baseados em strings é evitada.
Resumo
- Ao registrar um observador, o KVO cria dinamicamente a subclasse **NSKVONotifying_ e troca isa **
- A origem da notificação é o setter ** sobrescrito **, com willChange/didChange inseridos.
-classé manipulado para responder a classe original; a classe real aparece comobject_getClass().- Alterar o ivar diretamente ignora o setter e não gera notificações; se necessário, notifique manualmente com willChange/didChange.
- No Swift,
@objc dynamice a API observe baseada em blocos são o padrão; liberar o token remove o registro.
Ao considerar method swizzling e isa-swizzling, o runtime do Objective-C fica claro: tanto a tabela de métodos quanto a classe do objeto são dados que podem mudar em runtime. Essa flexibilidade está na origem de grande parte da magia dos frameworks Cocoa.
Continue lendo
- Method Swizzling em Objective-C: guia completo
- +load vs +initialize: momento da chamada e armadilhas da herança
- Categorias de Objective-C vs extensões de Swift: diferenças, conceitos e cuidados
Fontes e verificação
- Using Key-Value Observing in SwiftApple · Documentação oficial · Consultado 17 de agosto de 2026Evidência: Registro de KVO em Swift, notificações de alteração e ciclo de vida do token de observação

