[Delphi] Access Violation! O que fazer?

Olá, leitores! Wow! Sejam bem-vindos ao novo visual do blog! 🙂
Bom, como já faz um tempo que não elaboro artigos sobre Delphi, hoje é dia de publicar mais um tópico técnico! Imagine que você esteja realizando alguns testes funcionais no seu software, quando, de repente, ocorre um Access Violation! Você sabe como proceder para rastrear (e resolver) o problema?

O tal do Access Violation é um dilema para qualquer programador Delphi e virou até motivo de piada. Alguns ironizam, dizendo “Acesso violento” ou “Excel Violation”, haha. O problema é que, por ser uma mensagem de erro muito genérica, os Access Violations realmente nos deixam de cabelo em pé, principalmente em situações quando parece não ter um motivo aparente. A saída é respirar fundo e fazer uso de alguns recursos do Delphi para identificar a linha de código que está causando a violação.

Call Stack

Pois bem, e por falar em recursos, o Delphi disponibiliza uma ferramenta que captura a pilha de chamadas de métodos e nos ajuda bastante nessa rastreabilidade, chamada Call Stack.

Já ouviu falar? Não? Então, na próxima vez que você se deparar com um Access Violation, acesse o menu View > Debug Windows > Call Stack e observe a janela que será exibida. É uma lista que representa um “histórico” de chamadas de métodos, permitindo acompanhar o fluxo de operações que o sistema está executando até o levantamento da exceção. Confira o exemplo abaixo:

Exemplo de Call Stack

Ao analisar o Call Stack acima, é possível identificar que tudo se inicia no evento de clique de um botão e percorre vários outros métodos, de baixo para cima, até a parada do sistema. Ao clicar duas vezes no nome dos métodos exibidos na janela, o Delphi nos leva à linha de código que os chamam. Opa, assim fica mais simples rastrear o erro, não é?

Baixe o exemplo neste link, execute-o no Delphi e observe, pelo Call Stack, que a origem do problema está no método ConsultarDados.

Por que neste método?

Os erros de Access Violation, em grande maioria, são causados por acessos a endereços de memória que não existem, como, por exemplo, utilizar um objeto que não foi instanciado ou que já foi destruído. Considere o código abaixo:

Ao executar esse código, o que acontece? Access Violation! O motivo do erro é visível: o objeto do tipo TStringList não foi instanciado antes de ser utilizado, ou seja, esse objeto não existe na memória e não pode ser referenciado. Como solução, se adicionarmos a linha abaixo no início do método, o erro será solucionado:

Agora, uma reflexão: quando usamos uma ferramenta, como uma chave de fenda, o correto é devolvê-la no mesmo lugar onde estava, certo? Isso é bom senso. Pensando assim, já que criamos um objeto, também precisamos destruí-lo após a utilização, caso contrário, o espaço alocado na memória para esse objeto permanecerá preenchido. Isso pode causar quedas de desempenho, uso alto de memória RAM, Memory Leaks, ou, talvez, os erros de “Out of Memory”.

A destruição de objetos no Delphi pode ser realizada com o FreeAndNil:

Mas, cuidado! Após destruir o objeto, ele é liberado da memória e não deve mais ser utilizado. Como exemplo, o código abaixo também resultaria em um Access Violation:

Como minimizar os Access Violations?

Embora seja um erro de runtime, ou seja, que ocorre somente em tempo de execução, existem algumas técnicas de codificação que podem reduzir o risco da apresentação de Access Violations no sistema.

A primeira delas é o Assigned – função que identifica se o objeto passado por parâmetro está alocado na memória. Ao utilizá-lo, podemos evitar que uma operação com um objeto inexistente seja executada. No código a seguir, só obtenho as informações da lista caso o objeto exista:

Claro, é importante mencionar que não é nada apropriado utilizar o Assigned em todos os lugares que fazem referência a objetos, já que tornaria o código redundante e desnecessário. Utilize-o em pontos estratégicos, como em métodos que são chamados por vários locais no software. Um destes locais, talvez, pode não utilizar os objetos internos do método, portanto, se não utilizarmos o Assigned para verificar a existência, ocorrerá os erros de violação de acesso.

A segunda técnica é o esqueleto do Try/Finally, no qual já comentei no artigo sobre técnicas de tratamento de exceções. Adquira o hábito de escrever a estrutura completa do Try/Finally antes de partir para a codificação da regra do método. Além de garantir a destruição do objeto, esse hábito colabora para a confiabilidade do aplicativo e evita eventuais Access Violations, caso o objeto seja uma variável de classe.

A terceira técnica é a utilização do destructor da classe TObject para liberar todos objetos que foram instanciados, visando o desempenho e integridade do sistema. Atente-se que o destructor também é útil para desligar eventos, redefinir propriedades e desvincular Interfaces, quando necessário. Este método deve ser declarado como override (sobrescrito) na seção public da classe:

Na minha opinião, a atividade de instanciar e destruir os objetos de forma apropriada (e consciente) é um diferencial de um desenvolvedor, que, na verdade, não deixa de ser uma questão de pura responsabilidade.

Aproveitando o contexto deste artigo, vale apresentar o EurekaLog, um componente para Delphi que captura exceções no software e elabora um log muito bem detalhado, facilitando ainda mais a rastreabilidade do código defeituoso. Além disso, é possível configurá-lo para enviar o log para o e-mail do desenvolvedor no momento em que a exceção ocorre. Este envio pode ser ajustado para utilizar uma thread separada, dispensando a intervenção do usuário.

 

Obrigado pela atenção, leitores!
Abraço!


 

André Celestino