Tecnologia além do código.
Régys Borges da Silveira

Assigned ou <> nil? A Verdade Técnica por Trás da Escolha em Delphi

assigned_header
Neste artigo 7 tópicos

Uma das discussões mais antigas e recorrentes nos fóruns de Delphi e Pascal gira em torno de uma verificação simples: devemos usar Assigned(Variavel) ou Variavel <> nil?

À primeira vista, ambas parecem fazer exatamente a mesma coisa: verificar se um ponteiro ou referência de objeto contém um endereço válido de memória. Muitos desenvolvedores escolhem um ou outro baseando-se puramente em legibilidade ou hábito (“Assigned é mais elegante” vs “<> nil é mais explícito“).

No entanto, há uma razão técnica profunda para a existência da função Assigned, e ela remonta às origens do Delphi 1 e à arquitetura da VCL. Entender isso é crucial, especialmente se você desenvolve componentes.

O Básico: Ponteiros Normais e Objetos

Para ponteiros simples e instâncias de objetos, a verificação tradicional:

if MeuObjeto <> nil then
  // faz algo

…funciona perfeitamente. O compilador gera um código de máquina eficiente que compara o valor do ponteiro com zero. Assumindo que a variável foi inicializada corretamente (como nil), essa instrução cumpre seu papel.

Então, por que a Borland (hoje Embarcadero) introduziu a função Assigned?

O Pulo do Gato: Method Pointers

A necessidade do Assigned surgiu com a introdução dos Method Pointers (Ponteiros de Método), que são a base do sistema de eventos do Delphi (como o OnClick, OnChange, etc).

Diferente de um ponteiro simples que guarda apenas um endereço de memória, um Method Pointer é uma estrutura composta por dois ponteiros:

  1. Code Pointer: O endereço do método (código) a ser executado.
  2. Data Pointer: A instância específica do objeto (o Self) onde esse método será executado.

Em termos de memória, é como se fosse um record com dois campos.

O Problema do Design-Time

Aqui é onde a mágica (e o perigo) acontece. Quando você está desenhando uma tela no IDE do Delphi, o “VCL Form Designer” precisa realizar alguns truques.

Para gerenciar as propriedades no Object Inspector sem disparar eventos acidentalmente, o Designer do Delphi, às vezes, atribui valores internos a esses Method Pointers. Especificamente, ele pode preencher a parte do Data Pointer (a instância) com um índice interno ou marcador, deixando o Code Pointer vazio ou nulo.

Se você tentar verificar um evento usando a sintaxe padrão:

// Tenta verificar se ambos os ponteiros da estrutura são nil
if @FOnClick <> nil then 
  FOnClick(Self);

Essa verificação (<> nil) compara a estrutura inteira (ambos os ponteiros). Se o Designer do Delphi tiver preenchido a parte da instância com seus dados internos, a comparação resultará em True (diferente de nil), mesmo que não exista código real para executar. O resultado? O componente tenta chamar um método que não existe e BOOM: Access Violation ou comportamento indefinido dentro da IDE.

A Solução: Assigned

A função Assigned foi criada especificamente para resolver esse cenário. Ela não verifica a estrutura inteira do ponteiro de método.

Ao usar Assigned(FOnClick), o compilador gera código que verifica apenas o Code Pointer (o endereço do código), ignorando o ponteiro da instância.

Isso garante que:

  1. Se o evento foi atribuído no código, ele verifica o endereço do método corretamente.
  2. Se o Designer do Delphi “sujou” a parte da instância para uso interno, o Assigned ignora isso e retorna False corretamente (já que o endereço do código ainda é nil), prevenindo o erro.

Resumo Técnico

Ao nível de assembly e geração de código:

  • if @Event <> nil: Compara os dois blocos de memória (Code + Data) contra zero. Perigoso para componentes.
  • if Assigned(Event): Compara apenas o bloco de memória do código contra zero. Seguro para componentes.

Veredito: Quando usar qual?

Com base na arquitetura do compilador explicada por Allen Bauer (um dos arquitetos originais do Delphi), a regra de ouro é:

  1. Para Eventos (Method Pointers): O uso de Assigned é obrigatório. Se você está escrevendo um componente e vai disparar um evento (FOnClick), você deve usar if Assigned(FOnClick). Não faça if @FOnClick <> nil.
  2. Para Objetos e Ponteiros Comuns: É uma questão de preferência. Assigned(Obj) e Obj <> nil funcionam de forma idêntica e segura.
    • Alguns preferem Assigned pela consistência com os eventos.
    • Outros preferem <> nil para deixar claro que estão lidando com ponteiros e memória, ou porque acham mais legível ver a comparação explícita.

Conclusão

Embora pareça apenas “açúcar sintático”, o Assigned é uma ferramenta vital para a estabilidade da VCL em tempo de design. Adotar o Assigned como padrão para tudo pode criar uma consistência no código, mas entender por que ele existe separa o programador que apenas escreve código daquele que entende a ferramenta que usa.

Para seus componentes, não arrisque: vá de Assigned.

Fonte de referência: The Oracle at Delphi – Assigned or not Assigned

Compartilhar por e-mail

1 comentário(s)

  1. Ótima explicação Regys, como sempre uma boa didática ajuda bastante a entender um conceito com mais clareza. Eu sempre utilizo o Assigned como padrão exatamente para ter uma consistência no código.

Participe da conversa

Seu e-mail não será publicado.

Este site utiliza o Akismet para reduzir spam. Saiba como seus dados em comentários são processados.

Um próximo passo para cada interesse.

Continue estudando com os livros, acompanhe as videoaulas ou consulte as soluções para o seu projeto.

Descubra mais sobre Régys Borges da Silveira

Assine agora mesmo para continuar lendo e ter acesso ao arquivo completo.

Continuar lendo