Há uma dúvida muito comum que vejo entre os desenvolvedores Delphi, no que diz respeito à liberação de objetos da memória. Eu mesmo já tive alguns problemas com isso, e a intenção deste artigo é exatamente compartilhar com vocês essa experiência para evitar que tenham problemas devido à má liberação dos objetos da memória.

Temos 3 possibilidades para liberar objetos da memória.

1 – Destroy – Executa o destrutor da classe, liberando o objeto da memória, porém não remove a referência do objeto ao endereço de memória.
Isso pode ser constatado ao verificarmos a referencia do objeto em debug, veremos que ele continua fazendo referência a uma posição de memória, inclusive, se dermos outro destroy em seguida, ganhamos um “Acess Violation” de brinde do delphi.

Vamos tentar exemplificar com código. Por exemplo, se tivermos o seguinte contexto, um objeto do tipo Funcionário declarado na seção private do form. Antes de criarmos este objeto, verificamos se o mesmo já está criado, para então criarmos ele e alimentarmos com alguns valores, após isso, nós exibimos o nome do funcionário em um ShowMessage para então destruirmos o objeto.

 figura1.JPG

Na primeira vez que executarmos este bloco de código, o funcionário será igual a nil, então entrará na condição not(Assigned(Funcionario)), criaremos o funcionário e alimentaremos com os valores padrões. Após isso, o showmessage será executado exibindo o nome do funcionário e logo em seguida o objeto será destruído. Porém, se executarmos este bloco pela segunda vez, a condição not(Assigned(Funcionario)) não será satisfeita, pois Funcionario continuará com a referência ao endereço de memória, com isso ao executarmos o ShowMessage ganharemos um AccessViolation.

2 – Free – Verifica se o objeto é nil e caso seja, executa o destroy do objeto. Neste caso, o objeto também permanece com a referência à posição de memória, porém ele verifica se o objeto é nil antes de executar o destroy, tornando-o um pouco mais eficiente do que o Destroy.

No caso do Free, mesmo ele sendo mais eficiente do que o destroy, teríamos o mesmo problema do AccessViolation, pois o free também não se encarrega de atribuir nil ao objeto após a destruição do mesmo. Para não termos o Access violation e conseguirmos recriar o objeto Funcionario teríamos que alterar a forma de liberação do objeto para:

    Funcionário.free;
    Funcionário := nil;

    Com isso, na próxima vez, a condição not(Assigned(Funcionario)) será satisfeita e o objeto será recriado, assim não causará AccessViolation ao executar o ShowMessage;
    Lembrando que esta forma (atribuindo nil explicitamente) também funcionaria com o destroy.

3 – FreeAndNil – Este é o mais eficiente, ele executa o free do objeto passado como parâmetro e em seguida remove a referência do mesmo ao endereço de memória do objeto, atribuindo nil ao objeto, com isso, caso seja executado novamente o FreeAndNil no objeto, não receberemos nenhuma mensagem de erro, pois ele executará o free , que por sua vez verificará se o objeto é diferente de nil e somente se for é que executará o destroy, e após isso, atribuirá nil ao objeto em questão.

    Com o FreeAndNil, não precisamos atribuir explicitamente o nil ao objeto funcionário, pois o próprio método já se encarrega disso. Dessa forma a liberação do objeto ficaria assim:

    FreeAndNil(Funcionario);

Desta forma, ao executar o bloco de código da figura 1 pela segunda vez, a condição condição not(Assigned(Funcionario)) será satisfeita, pois o objeto será nil, e então criaremos novamente o objeto, alimentaremos os valores e a execução prosseguirá com sucesso.

Temos apenas que tomar um cuidado especial ao usar o Free e o FreeAndNil. Como foi dito, no fim das contas, ambos chamam o método Destrutor do objeto, porém, caso seu objeto tenha um destrutor diferente do padrão, é fundamental que o mesmo esteja com a diretiva override, pois senão, ao executar o free, será executado apenas o destroy default do TObject. Por exemplo, imagine que tenhamos a seguinte declaração da classe TFuncionario.

figura2.JPG
 
E a seguinte implementação no método destroy:

 
figura3.JPG


Nesse caso, se utilizarmos Funcionário.destroy, receberemos um showMessage com a mensagem informando que executou o destructor  do TFuncionario(pois executamos o destroy diretamente da classe concreta TFuncionario), porém se utilizarmos o Free ou o FreeAndNil veremos que a mensagem não será exibida, isto porque o destructor que foi executado foi da classe TObject e não da TFuncionario.

Agora, se alterar a assinatura do método, incluindo override, a história muda. Mesmo se executarmos o Free ou o FreeAndNil, receberemos ao mensagem informando que o  destructor executado foi da TFuncionario pois sobrescrevemos o método destroy (que é virtual na classe TObject).

Sabendo-se destas diferenças e tomando os devidos cuidados, tenho certeza que serão evitados muitos access violations aparentemente “misteriosos” em suas aplicações.

Bom pessoal, este foi um artigo breve, explicando as principais diferenças e cuidados básicos ao se liberar objetos da memória no Delphi, espero que tenham gostado, até a próxima.