Free vs. FreeAndNil vs. Release
Durante minha palestra sobre OO no ClubeDelphi TW várias pessoas me perguntaram sobre a diferença entre Free, FreeAndNil e Release. Como eu não sabia, pois sempre usei Free e nunca tive problemas, fiquei de pesquisar e comentar o que encontrasse. Portanto aqui estão meus comentários a respeito:
O Release só serve para forms, pois é introduzido na classe TCustomForm. Ele deve ser usado se você quer liberar um form de dentro dele mesmo, por exemplo, no evento OnClick de um botão no próprio form. O Release posta uma mensagem CM_RELEASE para o form (veja TCustomForm.Release na unit Forms), que quando processada, dispara o Free. A diferença é que o Release processa todas as mensagens e eventos que estiverem na fila do form antes que ele seja destruido. Disparar um Free de dentro do form, pode gerar um AV.
O Free deve ser usado quando você quer libera o form fora do contexto do form, por exemplo, isto está correto:
Form := TForm1.Create(Application);
Form.ShowModal;
Form.Free;
Usar o Release de fora do form também gera o mesmo resultado, porém de forma menos otimizada, pois a mensagem é postada para o form que depois chama o Free.
Form := TForm1.Create(Application);
Form.ShowModal;
Form.Release;
O FreeAndNil é apenas uma procedure que atribui nil a variável de instância e na sequência chama um Free, conforme se pode ver no seu código ultra-simples em SysUtils:
procedure FreeAndNil(var Obj);
var
Temp: TObject;
begin
Temp := TObject(Obj);
Pointer(Obj) := nil;
Temp.Free;
end;
Ele só é interessante de ser usado quando você precisa testar se o objeto está instanciado usando o Assigned. Se você chama apenas o Free a memória é liberada da mesma forma, sem vestígios, porém, se a variável não for reutilizada e ainda estiver dentro do escopo, continuará apontando para um endereço inválido. Isso não consome mais memória, e isso não é um problema se você não precisa mais da variável. Por isso aconselho sempre a usar variáveis com o menor escopo possível, pois dessa forma, você quase nunca precisa se preocupar com esse tipo de problema.
Exemplo 1: Variável implicita:
procedure TForm1.Button5Click(Sender: TObject);
begin
with TForm2.Create(nil) do
try
ShowModal;
finally
Free;
end;
end;
Neste caso você nem precisa se preocupar com variável nenhuma. Sei que muita gente odeia o with, mas em pequenos trechos como esse, não vejo desvantagens, e ele ainda me economiza a declaração da variável para o form. Não preciso usar Release pois estou liberando o form fora dele, e não preciso do FreeAndNil porque em nenhum momento preciso testar variável nenhuma para saber se existe uma instância.
Exemplo 2: Variável local:
procedure TForm1.Button4Click(Sender: TObject);
var
Form: TForm2;
begin
Form := TForm2.Create(Application);
Form.ShowModal;
Form.Free;
end;
O resultado é o mesmo do exemplo 1, porém usando uma variável local. Não precisamos do Release pelo mesmo motivo do anterior e não precisamos do FreeAndNil, pois ao final da procedure, a variável Form sai do escopo e morre. Não precisamos nos preocupar em atribuir nil a ela.
Exemplo 3: Form não destruido ao fechar
type
TMeuForm = class(TForm)
public
class procedure Mostrar;
end;
implementation
var
Form: TMeuForm;
{$R *.dfm}
{ TForm2 }
class procedure TMeuForm.Mostrar;
begin
if not Assigned(Form) then
Form := TMeuForm.Create(Application);
Form.Show;
end;
Se você precisa que um form fique na memória, pode usar este código acima. Ao ser chamado pela primeira vez, o Mostrar testa para ver se o form já está criado, Se não estiver ele cria e mostra, se já estiver criado, ele apenas mostra. Este form tem como owner o aplicativo, portanto ao finalizar seu aplicativo, o form será finalizado juntamente e você não precisa se preocupar com mais nada.
Exemplo 4: Form sem variável
Um form sem variável pode ser facilmente mostrado assim:
TMeuForm.Create(Application).Show;
ou assim:
TMeuForm.Create(Application).ShowModal;
E no OnClose do form, basta colocar:
procedure TMeuForm.FormClose(Sender: TObject; var Action: TCloseAction);
begin
Action := caFree;
end;
Com isso o form é liberado assim que for fechado.
Enfim, esses quatro exemplos atendem todas as minhas necessidades sem usar Release nem FreeAndNil. Felizmente eu já estava no caminho certo.




Muito interessante, porém gostaria de fazer uma resalva. No exemplo abaixo não vejo motivo para passar o Owner, pois você vai ser o responsável por destruir o objeto e não o Owner. Dê uma olhada no construtor de TComponent e veja o que acontece se passamos o Owner nil e entenderá o desperdício. Use assim:
with TForm2.Create(nil) do
try
ShowModal;
finally
Free;
end;
Eu costumo sempre passar o Owner como nil se eu for destruir o objeto manualmente, evitando um possível AV se por exemplo eu der um Free em finalization de uma unit. Se você instancia um objeto com Owner Application em Initialization por exemplo, e libera com free em finalization, tera um AV, pois o Application terá liberado a instância do objeto antes de seu free.
Abraço.
Você está certíssimo, eu já não passo mais o owner quando destruo manualmente o componente. Só me toquei disso algum tempo depois de escrever este artigo, e acabei me esquecendo de atualizar aqui.
Inclusive publiquei um post aqui sobre isso, veja:
http://www.ericksasse.com.br/?p=385
Estou corrigindo o artigo, obrigado pelo toque.
Olá Erick,
Estou com uma duvida cruel,
Uso bastante query dinamica e gostaria de saber se quando eu dou um freeandnil(query) o banco ao qual essa query estava conectada continua registrando a sua conexão ou quando destruo essa query a conexão é automaticamente fechada?????????
Então,,
Eu posso fazer simplesmente freeandnil(query)
Ou devo sempre fazer:
query.close;
freeandnil(query);
????????????????????????
Aldo, a melhor forma de tirar essa dúvida é olhar o código da VCL. Quando um TDataSet é destruído, ele chama o Close automaticamente, então não é necessário que você chame. Pode destruir diretamente.
Eu sempre uso FreeAndNil(); pois se eu precisar checar se a variável está instânciada sempre vou ter o resultado correto assim padronizando de forma clara e sem POG meu codigo, claro que cada caso é um caso, no meu esta foi a melhor forma que encontrei.
Olá Erik.
Gostaria de lhe perguntar se você sabe qual a maneira mais correta de liberar memória de objetos alocados usando o Delphi for .net? Pergunto isto porque tenho algumas DLLs feitas em delphi .net, que são chamadas apartir de aplicações win32 feitas em delphi, e estou tendo dificuldades para liberar memória de objetos que usam certificado digital e arquivos XML. Como no Delphi .net não tem a função FreeAndNil eu estou usando o Free e logo em seguida passando nil para o objeto, mas não esta resolvendo, pois depois que termina a execução da dll a memória continua alocada em memória! Se puder ajudar ficarei grato.
Olah erick,
tenho um caso que estah me tirando o sono. um sistema onde os forms de manutencoes sao dinamicos, isso eh, o mesmo form para dah manutencao em diversas tabelas. e nesses forms tem campos que chamam uma tela de consulta. na tela de consulta tem um botao (incluir) para no caso que a consulta nao for satisfeita o usuario dali mesmo ja poder buscar a tela de cadastro e incluir o que for necessario. podendo abrir varias telas sucessivamente.
Com apenas uma tela aberta apos a consulta (botao incluir), apos o retorno para o form origem nao dah erro. mas quando o usuario abre varias telas atravez das consultas ai no retorno do form origem dah erro.
Minha pergunta: Que vc me indicaria para melhor gerenciar esse problema???
Dificil entender sem ver código. Se quiser me mandar um pequeno projeto que reproduz esse cenário por e-mail, posso dar uma olhada.