reynosa - ¿Por qué se asignan los objetos Delphi incluso después de llamar a.Free?
delphi technologies mexico (4)
En Delphi, ¿por qué la función Assigned () aún devuelve True después de llamar al destructor?
El siguiente código de ejemplo escribirá "sl todavía está asignado" a la consola.
Sin embargo, puedo llamar a FreeAndNil (sl); y no será asignado.
He estado programando en Delphi por un tiempo, pero esto nunca tuvo sentido para mí.
¿Alguien puede explicar?
program Project1;
{$APPTYPE CONSOLE}
uses SysUtils, Classes;
var
sl : TStringList;
begin
sl := TStringList.Create;
sl.Free;
if Assigned(sl) then
WriteLn(''sl is still assigned'')
else
WriteLn(''sl is not assigned'');
end.
Intenté comparar las operaciones de VCL ... FreeAndNil es corto, dulce y tiene sentido:
procedure FreeAndNil(var Obj);
var
P: TObject;
begin
P := TObject(Obj);
TObject(Obj) := nil; // clear the reference before destroying the object
P.Free;
end;
Pero TObject.Free está en ensamblador misterioso, que no entiendo:
procedure TObject.Free;
asm
TEST EAX,EAX
JE @@exit
MOV ECX,[EAX]
MOV DL,1
CALL dword ptr [ECX].vmtDestroy
@@exit:
end;
El método gratuito de TObject es como el "operador de eliminación" en C ++. Llamar gratis primero llamará a la función Destruir y luego liberará el bloque de memoria que se asignó para el objeto. Por defecto, el puntero a la memoria no se pone a cero porque usará una instrucción.
Lo correcto en la mayoría de los casos es no establecer el puntero a cero porque en la mayoría de los casos no importa. Sin embargo, a veces es importante, por lo que solo debe anular el puntero para esos casos.
Por ejemplo. En una función en la que se crea un objeto y luego se libera al final de la función, no tiene sentido establecer la variable en cero, ya que eso solo está desperdiciando el tiempo de la CPU.
Pero para un objeto o campo global al que se pueda hacer referencia de nuevo más tarde, debe establecerlo en cero. Use FreeAndNil o simplemente configure el puntero a cero, no importa. Pero manténgase alejado de las variables de ajuste a cero que no necesitan ponerse a cero por defecto.
Los ''objetos'' de Delphi VCL en realidad son siempre indicadores de objetos, pero este aspecto generalmente está oculto para usted. Simplemente liberando el objeto deja el puntero colgando, por lo que debería usar FreeAndNil en su lugar.
El "misterioso ensamblador" se traduce aproximadamente a:
if Obj != NIL then
vmtDestroy(obj); // which is basically the destructor/deallocator.
Debido a que Free checks for NIL primero, es seguro llamar a FreeAndNil varias veces ...
Si usa sl.Free, el objeto se libera pero la variable sl aún apunta a la memoria que ahora no es válida.
Utilice FreeAndNil (sl) para liberar el objeto y borrar el puntero.
Por cierto, si lo haces:
var
sl1, sl2: TStringList;
begin
sl1 := TStringList.Create;
sl2 := sl1;
FreeAndNil(sl1);
// sl2 is still assigned and must be cleared separately (not with FreeAndNil because it points to the already freed object.)
end;
procedure TObject.Free;
asm
TEST EAX,EAX
JE @@exit // Jump to exit if pointer is nil.
MOV ECX,[EAX]
MOV DL,1
CALL dword ptr [ECX].vmtDestroy // Call cleanup code (and destructor).
@@exit:
end;
Tenemos reglas simples:
Si desea usar
Assigned()
para verificar si un objetoObj
ya está creado o no, asegúrese de usarFreeAndNil(Obj)
para liberarlo.Assigned()
solo dice si se asigna o no una dirección.A la referencia de objeto local siempre se le asigna una dirección de basura (alguna dirección aleatoria), por lo que es bueno establecerlo en cero antes de usarlo.
Ejemplo: (Este no es el código completo)
{Opened a new VCL application, placed a Button1, Memo1 on the form
Next added a public reference GlobalButton of type TButton
Next in OnClick handler of Button1 added a variable LocalButton
Next in body, check if GlobalButton and LocalButton are assigned}
TForm2 = class(TForm)
Button1: TButton;
Memo1: TMemo;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
GlobalButton: TButton;
end;
procedure TForm2.Button1Click(Sender: TObject);
var
LocalButton: TButton;
begin
if Assigned(GlobalButton) then
Memo1.Lines.Add(''GlobalButton assigned'');
if Assigned(LocalButton) then
Memo1.Lines.Add(''LocalButton assigned'');
end;