Perché gli oggetti Delphi vengono assegnati anche dopo aver chiamato.

In Delphi, perché la funzione Assigned () restituisce ancora True dopo aver chiamato il distruttore?

Il seguente codice di esempio scriverà “sl è ancora assegnato” alla console.

Tuttavia, posso chiamare FreeAndNil (sl); e non sarà assegnato.

Ho programmato in Delphi per un po ‘, ma questo non ha mai avuto senso per me.

Qualcuno può spiegare?

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. 

Ho provato a confrontare le operazioni VCL … FreeAndNil è breve e dolce e ha un senso:

 procedure FreeAndNil(var Obj); var P: TObject; begin P := TObject(Obj); TObject(Obj) := nil; // clear the reference before destroying the object P.Free; end; 

Ma TObject.Free è nel misterioso assemblatore, che non capisco:

 procedure TObject.Free; asm TEST EAX,EAX JE @@exit MOV ECX,[EAX] MOV DL,1 CALL dword ptr [ECX].vmtDestroy @@exit: end; 

Se si utilizza sl.Free, l’object viene liberato ma la variabile sl punta ancora alla memoria ora non valida.

Usa FreeAndNil (sl) per liberare l’object e cancellare il puntatore.

A proposito, se lo fai:

 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; 

Delphi VCL ‘oggetti’ sono in realtà sempre puntatori agli oggetti, ma questo aspetto è in genere nascosto da te. Liberare l’object lascia penzolare il puntatore, quindi dovresti usare FreeAndNil.

Il “Mysterious Assembler” si traduce approssimativamente in:

 if Obj != NIL then vmtDestroy(obj); // which is basically the destructor/deallocator. 

Poiché Free controlla per prima NIL, è sicuro chiamare FreeAndNil più volte …

Il metodo Free di TObject è come “delete operator” in C ++. Chiamando il libero chiamerà prima la funzione Destroy e quindi libererà il blocco di memoria che è stato assegnato per l’object. Di default il puntatore alla memoria non viene quindi azzerato perché questo utilizzerà una sola istruzione.

La cosa più corretta da fare nella maggior parte dei casi non è impostare il puntatore su zero perché nella maggior parte dei casi non ha importanza. Tuttavia, a volte è importante e quindi si dovrebbe solo nil il puntatore per quei casi.

Per esempio. In una funzione in cui un object viene creato e poi liberato alla fine della funzione, non ha senso impostare la variabile su zero poiché ciò sta solo sprecando tempo in cpu.

Ma per un object o campo globale a cui è ansible fare riferimento più tardi, è necessario impostarlo su zero. Usa FreeAndNil o semplicemente imposta il puntatore su zero, non importa. Ma stai lontano dalle variabili di azzeramento che non devono essere azzerate di default.

Abbiamo semplici regole:

  1. Se si desidera utilizzare Assigned() per verificare se un object Obj è già stato creato o meno, assicurarsi di utilizzare FreeAndNil(Obj) per liberarlo.

  2. Assigned() dice solo se un indirizzo è assegnato o meno.

  3. Il riferimento all’object locale è sempre assegnato a un indirizzo spazzatura (un indirizzo casuale), quindi è bene impostarlo su zero prima di usarlo.

Esempio: (Questo non è il codice 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;