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.
However, I can call FreeAndNil(sl); and it won't be assigned.
I've been programming in Delphi for a while, but this never made sense to me.
Can someone explain?
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.
I tried comparing the VCL operations... FreeAndNil is short and sweet and makes sense:
procedure FreeAndNil(var Obj);
var
P: TObject;
begin
P := TObject(Obj);
TObject(Obj) := nil; // clear the reference before destroying the object
P.Free;
end;
But TObject.Free is in mysterious assembler, which I don't understand:
procedure TObject.Free;
asm
TEST EAX,EAX
JE @@exit
MOV ECX,[EAX]
MOV DL,1
CALL dword ptr [ECX].vmtDestroy
@@exit:
end;
-
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. Besides FreeAndNil is threadsafe so it is not possible that another thread accesses the invalid 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;Roddy : Are you *sure* it's threadsafe? vmtDestroy could be easily called twice on the same object, so hopefully that *is* threadsafe.Gamecat : A single call to FreeAndNil is threadsafe. As are two calls. Note that the parameter is a var parameter and the pointer is first copied, then set to nil, and then freed. And note that free exits if the pointer is nil.Mattias Andersson : As written, in the original question, FreeAndNil() is unfortunately *not* threadsafe, because two threads could each copy the pointer then both proceed to free the object. FreeAndNil() should really use Interlocked Variable Access ( http://msdn.microsoft.com/en-us/library/ms684122(VS.85).aspx ) .Rob Kennedy : Calling FreeAndNil thread-safe really mars an otherwise good answer. If you're even at a point where you think you NEED thread-safety while freeing a shared object, you've got bigger problems in your code than you could ever hope for FreeAndNil to solve.Gamecat : @Rob Kennedy, agreed. But I have been maintaining other peoples code for too long to be afreaid of those problems.From Gamecat -
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...
From Roddy
0 comments:
Post a Comment