Quando você começa a estudar Objective-C, é comum parar por um instante diante do tipo id.
Surge a dúvida: “Que tipo é esse? Por que compila mesmo quando coloco qualquer objeto nele?”
Em resumo, id é um “ponteiro universal que pode apontar para qualquer objeto” e é a base da tipagem dinâmica e do duck typing no Objective-C.
Neste artigo, vamos entender exatamente o que é o tipo id, como a tipagem dinâmica e o duck typing funcionam no código real e quando usá-los ou ter cuidado.
Afinal, o que é o tipo id?
Em uma frase, id é um “ponteiro para um objeto Objective-C arbitrário”.
Você pode armazenar qualquer objeto sem especificar uma classe concreta, como NSString * ou NSArray *.
Na verdade, a essência de id é mais simples do que parece. Internamente, ele é definido assim.
// objc.h Definição interna (resumo)
typedef struct objc_object {
Class isa; // Ponteiro que indica a classe deste objeto
} *id;
Ou seja, id é um ponteiro para uma struct que contém um único ponteiro, isa.
Esse isa funciona como uma etiqueta que informa ao runtime: “Sou uma instância desta classe”.
Um detalhe interessante: id já é um ponteiro, então escrevemos id obj, sem adicionar um asterisco como em id *obj.
É fácil confundi-lo com NSString *str.
É assim que a tipagem dinâmica funciona
Tipagem dinâmica significa que “o tipo real do objeto é determinado em tempo de execução, não em tempo de compilação”.
A classe real do objeto armazenado em id é determinada durante a execução, observando o ponteiro isa.
Por isso, este código é possível.
id obj = @"Hello"; // Agora é NSString
NSLog(@"%@", [obj class]); // Saída: __NSCFConstantString
obj = @[@1, @2, @3]; // Agora mudou para NSArray
NSLog(@"%@", [obj class]); // Saída: __NSArrayI
A mesma variável, obj, muda de string para array.
O compilador não impede isso porque deixa a determinação do tipo real para o runtime.
O mesmo vale para o envio de mensagens. Ao chamar [obj length], o compilador não sabe, em tempo de compilação, se obj responderá a length.
Nesse momento, o runtime procura o método na classe de obj e o executa. Isso é chamado de despacho de mensagens.
A comparação fica assim.
- Tipagem estática: o compilador determina e verifica os tipos antecipadamente
- Tipagem dinâmica: o objeto real é analisado durante a execução
No Objective-C,
ide a tipagem dinâmica expressam a filosofia: “coloque primeiro e pergunte o que realmente é durante a execução”.
Qual é o verdadeiro significado de duck typing?
É aqui que muita gente se engana, resumindo duck typing a “não verificar tipos”.
A expressão original por trás do duck typing é:
“Se anda como pato e grasna como pato, é um pato.”
O importante não é a classe do objeto, mas “se ele consegue responder àquela mensagem”.
No Objective-C, você pode verificar isso diretamente com respondsToSelector:.
// Independentemente da classe, chame se responder a este método
if ([obj respondsToSelector:@selector(quack)]) {
[obj quack]; // Se grasna como pato, trate-o como pato
}
Não importa se obj pertence à classe Duck ou à classe Robot.
Se responder à mensagem quack, para o nosso código ele é um “pato”.
Isso é diferente do polimorfismo baseado em herança.
Mesmo sem herdar da mesma classe-pai, classes sem qualquer relação podem ser tratadas da mesma forma se tiverem o mesmo nome de método.
Essa flexibilidade é a base de projetos centrais do Cocoa, como o padrão delegate e target-action.
O id, tão conveniente quanto perigoso: quando ter cuidado?
id cobra um preço por sua liberdade.
Como o compilador quase não verifica tipos, enviar uma mensagem sem suporte passa pela compilação, mas derruba o app durante a execução.
É o famoso crash unrecognized selector sent to instance.
Por isso, na prática, usamos assim.
- Quando o tipo é conhecido, especifique uma classe concreta, como
NSString *, em vez deid - Se precisar usar
id, proteja a chamada comrespondsToSelector: - Defina um protocolo delegate para documentar que “responde a esta mensagem”.
Hoje, instancetype e genéricos como NSArray<NSString *> * reduziram bastante o uso indiscriminado de id.
Ainda assim, id continua pulsando como um coração no código de base dos frameworks e no runtime.
Em resumo, id representa a natureza dinâmica do Objective-C, e a tipagem dinâmica e o duck typing seguem a filosofia de “avaliar objetos pelo comportamento, não pela classe”.
No começo pode parecer estranho e perigoso, mas, ao entender o princípio, o design dos frameworks Cocoa fica muito mais claro.
Espero que id, que hoje deixou você travado, pareça um pouco menos assustador. Bom estudo de Objective-C!

