Previna AV com Assert
Uma das razões mais comuns para acontecer Access Violations (AV) nos programas é quanto se tenta acessar um método ou uma propriedade de um objeto que ainda não foi alocado ou que já foi desalocado da memória.
Por exemplo:
procedure AV; var List: TStringList; begin // AV porque List é um objeto não alocado List.Add('teste'); // ok, porque List foi previamente alocado List := TStringList.Create; List.Add('novamente'); // AV, porque o objeto foi desalocado e o valor // da variável List é um ponteiro inválido List.Free; List.Add('outra vez'); // AV da mesma forma que antes, apenas a // variável aponta para nil ($0000:0000) List := nil; List.Add('e finalmente'); end;
Essas são receitas certas pra cometer violações de acesso de leitura de memória. Normalmente, em blocos de código pequenos esse tipo de coisa não ocorre, porque temos mais visibilidade e controle sobre o tempo de vida das variáveis. O problema fica maior quando o objeto é um parâmetro recebido de outra parte qualquer do código. Quem garante que o valor passado como parâmetro é válido?
Só pra ficar claro:
procedure ShowText(List: TStringList); begin ShowMessage(List.Text); // AV? List é válido? end; procedure TestAV; var Strings: TStringList; begin ShowText(Strings); end;
Aqui TestAV chama ShowText passando como parâmetro um valor de objeto inválido, porque nunca foi alocado. Seja o valor igual a nil ou uma valor aleatório qualquer (dependendo do contexto, a variável pode conter “lixo” de memória), quando ShowText usar a propriedade Text do objeto vai produzir um AV.
O que eu uso para prevenir esses casos é um Assert básico.
O código acima com Assert ficaria assim:
procedure ShowText(List: TStringList); begin // Exceção (EAssert) caso List seja inválido Assert(List <> nil); // A partir daqui é seguro usar List ShowMessage(List.Text); end;
Assert tem como parâmetro um valor condicional (Boolean). Se falhar (False), dispara uma exceção EAssert. A vantagem é que, ao contrário de uma exceção de violação de acesso, a IDE vai te mostrar certinho onde ocorreu a violação, isto é, vai parar o debugger naquela linha do Assert. Sem uma verificação desse tipo, seria muito mais difícil identificar em todo o seu projeto o ponto que deu problema.
Se você quiser, pode ainda usar usar o segundo parâmetro opcional de Assert para dar uma mensagem ao usuário. Por exemplo: “Assert(List <> nil, ‘A função ShowText não recebeu um objeto TStringList válido’)”.
Outra vantagem do Assert é que depois que você estiver confiante da correção do código (quando esse dia chegar :) ), você poderá desativar um checkbox nas opções de compilação do Delphi para
simplesmente omitir todo o código de verificação de “Assert” globalmente no projeto (lembre-se de dar um Build depois que mudar essa opção).
Dá muito trabalho? “Better safe, than sorry.”
Que atire a primeira pedra quem nunca fez um “Access Violation”. ;-)
Comments
Deixe uma Resposta
