Ir para conteúdo

Arquivado

Este tópico foi arquivado e está fechado para novas respostas.

thales.saraiva

Liberar Form da Memoria!

Recommended Posts

Olá Colegas,

 

Estou usando o seguinte código:

 

 

//no evento " OnClose " do Form

 

Form1.Release;

Form1 := Nil;

 

 

Aí executo o programa e quando fecho o Form1 dá o seguinte erro:

 

Access violation at address 00437D33 in module 'Programa.exe'. Read of address 0000014C.

 

O que eu posso fazer para liberar o Form da memória da maneira correta?

Compartilhar este post


Link para o post
Compartilhar em outros sites

Action := caFree;
Tem tb:

Form1.Free;

Olá, eu estou fazendo quase que a mesma coisa.

Tenho na procedure de abertura do form:

 

procedure TfrmTeste.RegistrarTestes1Click(Sender: TObject);
begin
  Application.CreateForm(TfrmRegTes,frmRegTes); //cria o form
  frmRegTes.ShowModal; //mostra o form
  frmRegTes.Free; //libera o form da memória
end;

E na procedure do TfrmRegTes:

procedure TfrmRegTes.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  //fecha o form e libera memória
  action:=caFree;
end;

Só que quando eu executo uma procedure de armazenamento e afins eu precisava de limpar o que aconteceu no form. Uma parte da minha rotina está abaixo:

procedure TfrmRegTes.btFimClick(Sender: TObject);
var
  i :integer;
begin
  for i:=0 to Length(respostas)-1 do
	ShowMessage('idpro '+inttostr(respostas[i].idpro) + #13+
		'idcan '+inttostr(respostas[i].idcan) +#13+
		'idteste '+inttostr(respostas[i].idteste) +#13+
		'idsubfat '+inttostr(respostas[i].idsubfat));

  frmRegTes.Height:=230;
  gbHead.Enabled:=true;

  respostas:=nil;

  close();
end;

Como pode ver, estou usando um array dinâmico. Então, gostaria de assim que no final desta rotina, ao invés de fechar o form, ele voltar ao estado de abertura inicial.

Já tentei colocar uma rotina parecida com a de abertura ao invés do "close()" mas nisso dá o "access violation".

 

Tem alguma idéia do que pode ser feito?

 

Obrigado pela atenção.

Compartilhar este post


Link para o post
Compartilhar em outros sites

Boa noite a todos.

 

Ao Thales.Saraiva:

 

O método Release, presente na classe TCustomForm, serve somente para em caso de erro crítico ocorrido dentro de um Form qualquer, voce utilizá-lo que um destrutor recursivo do Form, ou seja, se ele é uma ação recursiva, ele tem que ser invocado de dentro do próprio Form que será destruído. O método Release, além de destruir o Form, ele primeiro atualiza todas as mensagens de API dos componentes no Form contido, bem como do próprio Form e depois destroi o próprio Form, assim sendo, atribuir o valor Nil a sua instância posteriormente é desperdício de recurso, pois voce está atribuindo Nil a uma Instância que já destruída.

 

Ao Caio M S Mancini.

 

Meu caro Caio, voce também está fazendo desperdício de recurso.

 

Como assim ?

 

Ou voce utiliza esta código, em sistemas SDI é claro:

 

procedure TfrmTeste.RegistrarTestes1Click(Sender: TObject);
begin
  try
	  Application.CreateForm(TfrmRegTes,frmRegTes); //cria o form
	  frmRegTes.ShowModal; //mostra o form
  finally
	  frmRegTes.Free; //libera o form da memória
  end;
end;

ou voce pode usar este tanto em sistemas SDI como em MDI (Aliás, em MDI voce só pode utilizar este).

 

procedure TfrmRegTes.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  //fecha o form e libera memória

  FreeAndNil(frmRegTes)  // Esta opção é a mais eficiente

  // ou 
  action:=caFree;

end;

Para abrir Forms em sistemas MDI, o recomendável é que voce só utilize uma linha de código assim:

 

Application.CreateForm(TfrmRegTes,frmRegTes);

Repare que voce também estava tentando destruir o mesmo Form duas vezes, e isto provavelmente gera a exceção de erro, pois como voce pode tentar liberar da memória uma instância que já foi destruída ?

 

Se voce pretende recuperar um form que foi fechado, então não o destrua da memória, apenas feche-o sem que o Evento OnClose esteja configurado para destruí-lo, quando voce cria o Form em memória, ele fica lá enquanto a aplicação estiver rodando, mesmo que ele esteja fechado. Voce pode também utilizar o método Hide do Form, que este o esconde sem destruí-lo, como se estivesse fechado. A única incoveniência disto é que se voce estiver com digamos 450 forms em memória, isto pode acarretar uma sobrecarga de memória, e ocasionar um estouro de pilha (Stack overflow).

 

 

Um abraço

Compartilhar este post


Link para o post
Compartilhar em outros sites

Olá Discorpio, obrigado por responder.

 

Então, testei colocando na procedure TfrmRegTes.btFimClick(Sender: TObject); o código do try ... finaly, mas não virou.

Quando clico no botão, ele cria um form limpo, mas não "mata" o anterior que continua aparecendo e na hora que fecho os dois dá a violação. Se mando fechar o anterior ae dá pau...

 

Por enquanto estou dando um close() no form assim que clico no botão, não é o ideal para a minha idéia, mas é o jeito de "quebrar" um galho.

 

Ah! E valeu pelo FreeAndNil, li sobre ele pela net que é mais eficaz mesmo.

 

Se souber de algo diferente que se possa fazer, valeu!

 

Obrigado.

Compartilhar este post


Link para o post
Compartilhar em outros sites

Bom dia Caio Mancini

 

Então neste caso, teste o FreeAndNil lá dentro do Bloco Finally, assim:

 

procedure TfrmTeste.RegistrarTestes1Click(Sender: TObject);
begin
  try
	  Application.CreateForm(TfrmRegTes,frmRegTes); //cria o form
	  frmRegTes.ShowModal; //mostra o form
  finally
	  FreeAndNil(frmRegTes); //libera o form da memória
  end;
end;

Para te dizer a verdade, eu também sempre desconfiei do método Free, pois acredito que ele destroi o Objeto mas não limpa a Instância, ou seja, a Instância fica fazendo referência a um objeto inexistente na memória.

 

Como assim ?

 

Todo Objeto é uma instância (variável) na memória, porém essa instância (variável) não armazena o objeto em si, e sim a posição (referência) de memória onde o objeto está armazenado, e por isso que acredito, muito embora ainda com dúvidas, de que o método Free destrói o objeto mas não limpa a instância (variável). Se não fosse assim, porque criaram o método FreeAndNil(Nome da Instância) ?

 

Só para te lembrar, se for utilizar o código acima, então aborte o uso da destruição do Form no Evento OnClose do mesmo, se não voce vai tentar destruir o mesmo Form duas vezes, e isto gera erro.

 

Um abraço

 

Jorge da Silva Abreu

Compartilhar este post


Link para o post
Compartilhar em outros sites

Então rapaz.

 

O que está acontecendo é que está apenas criando um novo form.

 

Eu coloquei exatamente o último código que me enviou. E ele cria um novo form por cima do que existia antes.

Não há como fazer tipo um "repaint" do form? Tipo... para ele voltar ao estado anterior?

 

Pensei... se eu der um FreeAndNil antes do código de criação do form, dentro do TRY dá pau?

 

Vou explicar o porquê: neste form há uma espécie de registro de testes. Então são criados diversos componentes dinamicamente. Então quando eu clico no "Registrar teste" gostaria que o form ficasse como estava de início para que eu registrasse outro teste.

 

Só para efeito de entendimento vou colocar a procedure completa, ficou bem legal, ainda mais com os esquemas de criação de componentes em tempo de execução:

 

Então tenho a procedure que cria os componentes:

procedure TfrmRegTes.btAbreTesteClick(Sender: TObject);
var
  rdg:TRadioGroup;
  y,i:integer;
begin
  gbHead.Enabled:=false;

  y:=0;
  i:=0;

  //seleciona o fator do teste do processo
  with ZQuery1 do
  begin
	Close;
	SQL.Clear;
	SQL.Add('select f.idfat,f.desfat,count(s.idfat) as qtd '+
	  'from teste t,fator f,subfator s '+
	  'where t.idteste=f.idteste '+
	  'and f.idfat=s.idfat '+
	  'and t.idteste='+inttostr(codTes)+' '+
	  'group by f.idfat '+
	  'order by f.idfat');
	Open;
  end;

  //monta os RadioGroup com base nos fatores do teste
  while not ZQuery1.Eof do
  begin
	rdg:=TRadioGroup.Create(self);
	rdg.Parent:=self;
	rdg.Visible:=false;
	rdg.Left:=8;
	rdg.Top:=200+y;
	rdg.Width:=849;
	rdg.Height:=81;
	rdg.Columns:=ZQuery1.FieldValues['qtd'];
	rdg.Name:='rg'+inttostr(i);
	//inttostr(ZQuery1.FieldValues['idfat']);
	rdg.Caption:=ZQuery1.FieldValues['desfat'];

	//seleciona o subfator do fator do teste do processo
	with ZQuery2 do
	begin
	  Close;
	  SQL.Clear;
	  SQL.Add('select s.idsubfator,s.dessub '+
		'from fator f,subfator s '+
		'where f.idfat=s.idfat '+
		'and s.idfat='+inttostr(ZQuery1.FieldValues['idfat'])+'');
	  Open;
	end;

	while not ZQuery2.eof do
	begin
	  rdg.Items.AddObject(ZQuery2.FieldValues['dessub'],TObject(strtoint(Zquery2.FieldValues['idsubfator'])));
	  ZQuery2.Next;
	end;
	rdg.ItemIndex:=-1;

	rdg.Visible:=true;
	rdg.OnClick:=guardaClique;
	ZQuery1.Next;
	i:=i+1;
	y:=y+90;
  end;

  frmRegTes.Height:=647;
  btFim.top:=200+y;
  btFim.Visible:=true;
end;

 

 

E tenho a procedure que registra os testes, e é a que eu quero que após executada faça o form voltar ao estado inicial. A única maneira que pensei nisso acontecer seria liberá-lo da memória e carregá-lo novamente.

 

procedure TfrmRegTes.btFimClick(Sender: TObject);
var
  i :integer;
begin
  for i:=0 to Length(respostas)-1 do
  begin
	{ShowMessage('idpro '+inttostr(respostas[i].idpro) + #13+
		'idcan '+inttostr(respostas[i].idcan) +#13+
		'idteste '+inttostr(respostas[i].idteste) +#13+
		'idsubfat '+inttostr(respostas[i].idsubfat));}

	//insere a nova coleção
	with ZQuery1 do
	begin
	  Active:=False;
	  Sql.Clear;
	  Sql.Add('insert into respostas(idsubfator,idpro,idteste,idcan,datres) '+
		'values ('+inttostr(respostas[i].idsubfat)+','+inttostr(respostas[i].idpro)+','+inttostr(respostas[i].idteste)+','+inttostr(respostas[i].idcan)+',:data)');
	  ParamByName('data').AsDateTime:=Date;
	  ExecSql;
	end;
  end;
  ShowMessage('Teste registrado com êxito!');

  frmRegTes.Height:=230;
  gbHead.Enabled:=true;

  respostas:=nil;

  try
	Application.CreateForm(TfrmRegTes,frmRegTes); //cria o form
	frmRegTes.ShowModal; //mostra o form
  finally
	FreeAndNil(frmRegTes); //libera o form da memória
  end;

end;

Se assim pôde entender melhor o que está acontecendo e tiver mais alguma idéia, ficarei eternamente agradecido!

 

Abraços!

Compartilhar este post


Link para o post
Compartilhar em outros sites

Boa tarde a todos.

 

Caio Mancini, só agora entendi o que voce quer fazer, na verdade voce

está criando componentes dinamicamente dentro do Form frmRegTes, e quer

salvar o Form junto com estes componentes criados para poder recuperá-los

mais tarde. Se isto é possível ?, pois é possível sim,

para fazer isto, voce terá que mexer com um arquivo que tem o nome da Unit do

seu Form e com a extensão .DFM (Delphi Form), pois este arquivo contém a

configuração do seu Form escrita mais ou menos assim:

 

object Form2: TForm2
  Left = 331
  Top = 322
  Align = alCustom
  BorderStyle = bsDialog
  ClientHeight = 92
  ClientWidth = 341
  Color = clSkyBlue
  Font.Charset = DEFAULT_CHARSET
  Font.Color = clWindowText
  Font.Height = -11
  Font.Name = 'MS Sans Serif'
  Font.Style = []
  FormStyle = fsStayOnTop
  OldCreateOrder = False
  Position = poScreenCenter
  PixelsPerInch = 96
  TextHeight = 13
  object lblPrompt: TLabel
	Left = 8
	Top = 8
	Width = 53
	Height = 13
	Caption = 'lblPrompt'
	Font.Charset = DEFAULT_CHARSET
	Font.Color = clWindowText
	Font.Height = -11
	Font.Name = 'MS Sans Serif'
	Font.Style = [fsBold]
	ParentFont = False
  end
  object MaskEdit: TMaskEdit
	Left = 8
	Top = 24
	Width = 329
	Height = 24
	CharCase = ecUpperCase
	Font.Charset = DEFAULT_CHARSET
	Font.Color = clWindowText
	Font.Height = -13
	Font.Name = 'MS Sans Serif'
	Font.Style = []
	ParentFont = False
	TabOrder = 0
  end
  object btnOK: TBitBtn
	Left = 72
	Top = 56
	Width = 75
	Height = 25
	TabOrder = 1
	OnClick = btnOKClick
	Kind = bkOK
  end
  object btnCancel: TBitBtn
	Left = 200
	Top = 56
	Width = 75
	Height = 25
	TabOrder = 2
	OnClick = btnCancelClick
	Kind = bkCancel
  end
end

Os Arquivos *.DFM, se não me falhe a memória, até o Delphi 5,

eram arquivos binários. A partir do Delphi 6 estes aquivos, muito embora

continuando com a extensão .DFM, passarão a ser do tipo Texto.

Qual foi a vantagem disto? Isso é ótimo na verdade para sistemas de

controle de versão. Se tenho um form com 100 componentes o DFM vai ficar

gigante, com centenas de linhas. Se eu mudo apenas um componente de lugar no form,

mesmo que seja um componente não visual, o DFM é atualizado.

Em casos como esse, apenas uma ou duas linhas do DFM são alteradas.

Se ele estiver no formato texto, os sistemas de controle de versão mais

inteligentes como o SubVersion armazenam apenas as modificações

realizadas de uma revisão para outra, ou seja, uma ou duas linhas.

Se o arquivo é binário, ele não consegue comparar, e tem que copiar

novamente o arquivo inteiro para seu repositório. Isto proporcionou

uma economia enorme

Mas outra grande vantagem é que as versões do seu DFM também poderão

ser comparadas por softwares que comparam código fonte, e você poderá

ver com facilidade as mudanças em propriedades dos seus componentes de

uma versão para outra.

 

Graças a isso, o Delphi disponibilizou duas funções que converte de

binário para texto (ObjectBinaryToText(Input: TStream, Output: TStream)),

e vice e versa (ObjectTextToBinary(Input: TStream, Output: TStream)).

Assim voce pode transformar o seu arquivo texto (possível *.DFM em um componente).

 

Vamos a código. Voce vai declarar as duas funções de conversão e implementá-las assim:

 

type
  TfrmRegTes = class(TForm)
	....
	....
	btnGravaComp: TButton;
	procedure btnGravaCompClick(Sender: TObject);
  private
	{ Private declarations }
	function ComponentToString(Component: TComponent): string;
	function StringToComponent(Value: string): TComponent;
  public
	{ Public declarations }
  end;

var
  frmRegTes: TfrmRegTes;

implementation

{$R *.dfm}

procedure TfrmRegTes.btnGravaCompClick(Sender: TObject);
var Arq: TextFile;
	Path: String; 
begin
  // Supondo que o nome da Unit do frmRegTes seja RegTes
  Path := ExtractFilePath(Application.ExeName) + 'RegTes.dfm'; 
  AssignFile(Arq, Path);
  if not FileExists(Path) then begin
	 Rewrite(Arq);
	 Append(Arq);
  end;
  Self.Visible := False;
  write(Arq, ComponentToString(Self));
  CloseFile(Arq);
  Self.Close;
end;

function TfrmRegTes.ComponentToString(Component: TComponent): string;
var
  BinStream:TMemoryStream;
  StrStream: TStringStream;
  s: string;
begin
  BinStream := TMemoryStream.Create;
  try
	StrStream := TStringStream.Create(s);
	try
	  BinStream.WriteComponent(Component);
	  BinStream.Seek(0, soFromBeginning);
	  ObjectBinaryToText(BinStream, StrStream);
	  StrStream.Seek(0, soFromBeginning);
	  Result:= StrStream.DataString;
	finally
	  StrStream.Free;
	end;
  finally
	BinStream.Free
  end;
end;

function TfrmRegTes.StringToComponent(Value: string): TComponent;
var
  StrStream:TStringStream;
  BinStream: TMemoryStream;
begin
  StrStream := TStringStream.Create(Value);
  try
	BinStream := TMemoryStream.Create;
	try
	  ObjectTextToBinary(StrStream, BinStream);
	  BinStream.Seek(0, soFromBeginning);
	  Result := BinStream.ReadComponent(nil);
	finally
	  BinStream.Free;
	end;
  finally
	StrStream.Free;
  end;
end;

....
....

 { Aqui, a final da Unit, voce vai ter que registrar a classe 
   dos componentes recém criados, que no seu caso são os TRadioButton, 
   assim: }

initialization
RegisterClass(TRadioButton);

end.

Só devo te alertar que tenha muito cuidado quando voce mexer com os arquivos *.DFM,

pois eles contém as configurações do seu Form, tanto até que quando voce acessar o

Form pela segunda vez, o Delphi lhe questionará se voce quer manter a atualização

anterior, responda que não.

 

Repare que muito embora eu não esteja utilizando a função StringToComponente

(..., ....), pois creio eu que voce não irá precisar por enquanto, porém já

estou te enviando o código e para quando futuramente voce queira recuperar

algum componente que tenha gravado o seu código como texto, isto é, para o

caso de voce quiser grava somente os componentes TRadioButton ou qualquer

outro componente, pois no caso do form não irá precisar. Para recuperar o

arquivo componente texto, o código é esse:

 

procedure TForm4.btnRecuperaCompClick(Sender: TObject);
var Comp: TComponent;
	Arq: TextFile;
	Linha, CompStr: String;
begin
  CompStr := '';
  AssignFile(Arq, 'NomeComponente.txt');
  Reset(Arq);
  while not Eof(Arq) do begin
	ReadLn(Arq, Linha);
	CompStr := CompStr + linha + #13;
  end;
  CloseFile(Arq );
  Comp := Form3.StringToComponent(CompStr);
  if Comp is TRadioButton then TRadioButton(Comp).Parent := Self;
end;

 

Um abraço

 

Jorge da Silva Abreu

Compartilhar este post


Link para o post
Compartilhar em outros sites

Só uma pequena correção, nesse código:

 

...
  ...
  ...
  if not FileExists(Path) then begin
	  Rewrite(Arq);
	 Append(Arq);
  end;
  ...
  ...
  ...
  // Corrigir tirando o operado NOT, assim:

  ...
  ...
  ...
  if FileExists(Path) then begin
	 Rewrite(Arq);
	 Append(Arq);
  end;
  ...
  ...
  ...

Um abraço

 

Jorge da Silva Abreu

Compartilhar este post


Link para o post
Compartilhar em outros sites

×

Informação importante

Ao usar o fórum, você concorda com nossos Termos e condições.