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.
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.
E a seguinte implementação no método destroy:
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.