In Delphi, why does the Assigned() function still return True after I call the destructor?
The below example code will write \"sl is still assigned\" to the console.
We have simple rules:
If you want to use Assigned()
to check if an object Obj
is already created or not,
then make sure you use FreeAndNil(Obj)
to free it.
Assigned()
only says if an address is assigned or not.
The local object reference is always assigned a garbage address (some random address), so it is good to set it to nil before using it.
Example: (This is not the full code)
{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;
The Free method of TObject is like the "delete operator" in C++. Calling free will first call the Destroy function and then it will free the memory block that was allocated for the object. By default the pointer to the memory is not then set to zero because this will use up one instruction.
The correct thing to do in most cases is not to set the pointer to zero because in most cases it does not matter. However, sometimes it is important and so you should only nil the pointer for those cases.
For example. In a function where an object is created and then freed at the end of the function there is no point in setting the variable to zero since that is just wasting cpu time.
But for a global object or field that might be referenced again later you should set it to zero. Use FreeAndNil or just set the pointer to nil yourself, does not matter. But stay away from zeroing variables that do not need to be zeroed by default.
If you use sl.Free, the object is freed but the variable sl still points to the now invalid memory.
Use FreeAndNil(sl) to both free the object and clear the pointer.
By the way, if you do:
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 'objects' are actually always pointers to objects, but this aspect is typically hidden from you. Just freeing the object leaves the pointer dangling around, so you should use FreeAndNil instead.
The "Mysterious Assembler" translates roughly to:
if Obj != NIL then
vmtDestroy(obj); // which is basically the destructor/deallocator.
Because Free checks for NIL first, it's safe to call FreeAndNil multiple times...