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.
TPodcast
8 Comments
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.