como saber se um ponteiro não-nil tem uma referencia valida?

Delphi

05/10/2006

Boa tarde a todos:
Meu problema é o seguinte: fazer uma função que execute o método free em todos os objetos de um array de objetos.

procedure LimpaTudo(vObjetos: array of TObject);
var
  i: Integer;
  o: TObject;
begin
  try
    for i := Low(vObjetos) to High(vObjetos) do
    begin
      if not isNil(vObjetos[i]) then
      begin
        o := TObject(vObjetos[i]);
        MessageBox(0, PChar(string(o.ClassName)), ´limpando´, 0);
        Pointer(vObjetos[i]) := nil;
        o.Free;
        Pointer(o) := nil;
      end;
    end;
  except
  end;
end;


a função isnil está abaixo:

function isNil(pObjeto: tobject): boolean;
begin
  if  (pobjeto = nil) then MessageBox(0, ´objeto = nil´, ´teste´, 0) else MessageBox(0, ´objeto <> nil´, ´teste´, 0);
  if  (not assigned(pobjeto)) then  MessageBox(0, ´objeto not assigned´, ´teste´, 0) else MessageBox(0, ´objeto assigned´, ´teste´, 0);;
  isNil := ((pobjeto = nil) or (not assigned(pobjeto)));
end;


ela me retorna true caso o objeto seja nil ou caso ele não esteja assigned.

o problema é que se eu coloco pra liberar 3 vezes o mesmo objeto, tipo assim :

LimpaTudo([Edit1, Edit1, Edit1]);


ocorre access violation, pois isnil retorna true sempre. O que eu faço?


Vitor Rubio

Vitor Rubio

Curtidas 0

Respostas

Massuda

Massuda

05/10/2006

Você não precisa testar se é nil antes de dar Free. Free é (acho) o único método que tem tratamento para o caso do objeto ser nil. Assim, este código executa sem problemas...
var
  O: TObject;
...
  O := nil
  O.Free;
...
Por outro lado, testar se um objeto é válido é bem mais complicado. Dê uma olhada neste artigo [url=http://hallvards.blogspot.com/2004/06/hack-6checking-for-valid-object.html]Checking for a valid object instance[/url] (em inglês); note que o código sugerido pode falhar dependendo da versão do Delphi.


GOSTEI 0
Siam

Siam

05/10/2006

Acho que o problema é que vc está atribuindo nil após o Free para uma variável local e não para o seu array original de objetos.


GOSTEI 0
Siam

Siam

05/10/2006

Uma forma mais simples sem precisar testar seria:
procedure LimpaTudo(var vObjetos: array of TObject);
var i:Integer;
begin
  for i := 0 to High(vObjetos) do
    FreeAndNil(vObjetos[i]);
end;
Com isso o parâmetro [b:75e6dfa84a]var[/b:75e6dfa84a] fará com que seu array de origem seja atualizado para nil e o FreeAndNil fará o teste antes.


GOSTEI 0
Vitor Rubio

Vitor Rubio

05/10/2006

Você não precisa testar se é nil antes de dar Free.


Mas se eu der um free num objeto invalido, dá access violation. Testa meu código pra você ver.

Acho que o problema é que vc está atribuindo nil após o Free para uma variável local e não para o seu array original de objetos.

Talvez seja isso, mas eu queria passar um array na hora. Se eu tivesse que passar um array por referência, teria que declarar um array primeiro e preenche-lo, e depois passa-lo.

Então no meu caso

[quote]procedure LimpaTudo(var vObjetos: array of TObject); 
var i:Integer; 
begin 
  for i := 0 to High(vObjetos) do 
    FreeAndNil(vObjetos[i]); 
end;


não daria certo, porque eu não tenho o array, estou criando na hora, como se fosse um array constante.

Existe algum outro jeito de se passar parametros infinitos para uma função?

como eu crio uma function igual a writeln do pascal antigo, que aceita um número variado-indefinido de parametros?

quero construir uma função que de free em varios objetos de uma vez sem usar listas.


GOSTEI 0
Massuda

Massuda

05/10/2006

[quote:a5fa989b2d=´vitor^_^´]Mas se eu der um free num objeto invalido, dá access violation. Testa meu código pra você ver.[/quote:a5fa989b2d]Um valor ser nil é diferente de ser inválido.

O valor nil equivale numericamente ao zero. É fácil de testar. Free testa se o objeto é nil antes de chamar Destroy.

Um valor diferente de nil (numericamente não zero) pode ou não ser um objeto válido. O artigo que indiquei mostra como testar se um valor não-nil é ou não um objeto válido.


GOSTEI 0
Siam

Siam

05/10/2006

Não sei como estão sendo criados e destruídos esses objetos, mas outra alternativa seria utilizar um TList para mandar liberá-los.


GOSTEI 0
Vitor Rubio

Vitor Rubio

05/10/2006

Um valor ser nil é diferente de ser inválido. O valor nil equivale numericamente ao zero. É fácil de testar. Free testa se o objeto é nil antes de chamar Destroy.

Então, isso eu sabia. Mas como você deve ter visto no meu código, o problema é quando eu tenho duas variaveis diferentes, ou posições diferentes de um array de objetos, apontando para o mesmo objeto. Se eu der um free, ele vai liberar o objeto. Setando nil, ele vai setar só essa variavel pra nil, mas a ´copia´ dela não. A copia vai ficar com um ponteiro inválido, onde eu não posso dar free, e o assigned retorna true.

Precisaria de uma forma de liberar varios objetos de uma vez, com uma unica função.


Um valor diferente de nil (numericamente não zero) pode ou não ser um objeto válido. O artigo que indiquei mostra como testar se um valor não-nil é ou não um objeto válido.

Não entendi o artigo, ele disponibiliza uma biblioteca que só funciona em delphi 3 e que precisa de outras bibliotecas. A função principal dessa biblioteca usa uma outra que não está disponível.


GOSTEI 0
Massuda

Massuda

05/10/2006

[quote:d418bd128d=´vitor^_^´]Não entendi o artigo, ele disponibiliza uma biblioteca que só funciona em delphi 3 e que precisa de outras bibliotecas. A função principal dessa biblioteca usa uma outra que não está disponível.[/quote:d418bd128d]No final do artigo, ele dá outra função, mais simples, ValidateObj(), que não tem relação com o código que ele apresenta no início do artigo. Me parece (não testei) que ela deve resolver seu problema.


GOSTEI 0
Vitor Rubio

Vitor Rubio

05/10/2006

A função é esta:

function ValidateObj(Obj: TObject): Pointer;
type
  PPVmt = ^PVmt;
  PVmt = ^TVmt;
  TVmt = record
    SelfPtr : TClass;
    Other   : array[0..17] of pointer;
  end;
var
  Vmt: PVmt;
begin
  Result := Obj;
  if Assigned(Result) then
    try
      Vmt := PVmt(Obj.ClassType);
      Dec(Vmt);
      if Obj.ClassType <> Vmt.SelfPtr then
        Result := nil;
    except
      Result := nil;
    end;
end;


ela não funciona nesse caso:

procedure TForm1.Button1Click(Sender: TObject);
var o: TEdit;
begin
  o := Edit1;
  edit1.Free;
  ValidateObj(o);
  o.Free;
end;


que é o caso da minha procedure para limpar um array de objetos, não pode ter objetos repetidos. Alem disso, essa função é meio mistica: não entendi o porque de declarar PPVmt = ^PVmt; se não usa e tambem o porque que o ´Other : array[0..17] of pointer;´ é um array de 18 ponteiros....

de qualquer forma, valew!


GOSTEI 0
Siam

Siam

05/10/2006

E se vc mantivesse um TList de objetos liberados e na hora de liberar, checaria se ele já foi adicionado na lista.


GOSTEI 0
Massuda

Massuda

05/10/2006

Porque você não tenta usar algo mais simples...
procedure LimpaTudo(vObjetos: array of TObject); 
var 
  i: Integer; 
begin 
  for i := Low(vObjetos) to High(vObjetos) do begin 
    try
      vObjetos[i].Free;  
    except 
      // ignora problemas ao destruir
    end; 
  end;
end;
Eu não gosto desse código, mas deve funcionar no seu caso.

Duas perguntas:

* porque o array contem elementos duplicados?
* poderia dar uma idéia de como isso é usado?


GOSTEI 0
Vitor Rubio

Vitor Rubio

05/10/2006

E se vc mantivesse um TList de objetos liberados e na hora de liberar, checaria se ele já foi adicionado na lista.

É uma ótima solução mas eu não queria usar Tlist nesse caso.

Porque você não tenta usar algo mais simples...Código: procedure LimpaTudo(vObjetos: array of TObject); var i: Integer; begin for i := Low(vObjetos) to High(vObjetos) do begin try vObjetos[i].Free; except // ignora problemas ao destruir end; end; end; Eu não gosto desse código, mas deve funcionar no seu caso.


isso funcionaria mais ou menos, mas ele ficaria parando nos exceptions, durante o debug, atrapalhando. As vezes vc pode ate confundir cm um erro de verdade.

Duas perguntas: * porque o array contem elementos duplicados? * poderia dar uma idéia de como isso é usado?


Na verdade, um objeto repetido foi passado por engano, mesmo assim, não deveria dar essa exception ao liberar um objeto que já foi liberado. Se analisar a fundo, a minha procedure é mesmo inutil :oops: , mas quando deu esse erro surgiu essa curiosidade. O que eu queria era, ao inves de fazer:

objeto1.free;
objeto2.free;
objeto3.free;
...
...
...
objeton.free;


fazer:
Limpatudo(objeto1, objeto2, objeto3....);


eu nem vou usar mais essa procedure. Valew pela ajuda!


GOSTEI 0
POSTAR