MyList.Objects[SomeValidIndex].Free;
What the difference between "Free" and "FreeInstance" ? Does it suppress the allocated memory for Objects ?
TObject.FreeInstance is the low level part that really frees the memory and thus is the counterpart to
TObject.NewInstance. It is called at the end of the destructor and calling it manually can be considered a programming error in nearly all cases.
TObject.Free checks whether
Self is not
Nil and then calls the destructor
TObject.Destroy.
FreeAndNil is recommended in the FPC documentation here:
It is bad programming practice to call Destroy directly. It is better to call the Free method, because that one will check first if Self is different from Nil.
To clean up an instance and reset the reference to the instance, it is best to use the FreeAndNil function
Read the quoted sentence again:
To clean up an instance and reset the reference to the instance …. So this is about specific circumstances. Cause, well, it's one less line of code.
The "clean" (for lack of a better word) way is to use FreeAndNil. The reason is very simple, a class is a pointer to a heap memory block. Once the memory block has been freed, it should not be accessed again. If the class/pointer has not been nil-ed then it is possible to dereference the pointer after the memory has been freed, which is a programming error but, it often won't be visible immediately. if the pointer is nil-ed then any subsequent de-reference will cause an access violation (as it should) revealing the programming error right then and there.
I'd be happier if it were documented as being indivisible.
Since it's not, there's the potential for an avoidable race condition when the pointer is first inspected.
Documenting it as indivisible would not change that
FreeAndNil simply
is not indivisible. If you access the to be freed instance from multiple threads then you need to use synchronization.
I don't condemn FreeAndNil, but I try to avoid it as much as possible because it can hide bugs. Everybody thinks exceptions are a bad thing - no they are here to help you to find bugs. Checking freed pointers for nil is a habit which prevents exceptions - but along with automatic nilling deallocated pointers this prevents you from seeing the bug.
Not setting the reference to
Nil can be just as problematic and even harder to debug, namely when the memory location the instance is pointing to is reused. Then there'll be either exceptions or subtle bugs. Take the following example and imagine it in a more complex setting:
program treuse;
{$mode objfpc}
type
TTest1 = class
f: LongInt;
end;
TTest2 = class
f1, f2: UInt16;
end;
var
t1: TTest1;
t2: TTest2;
begin
t1 := TTest1.Create;
t1.f := 42;
Writeln(HexStr(Pointer(t1)), ' ', HexStr(t1.f, SizeOf(LongInt) * 2));
t1.Free;
t2 := TTest2.Create;
t2.f1 := 1;
t2.f2 := 5;
Writeln(HexStr(Pointer(t1)), ' ', HexStr(t1.f, SizeOf(LongInt) * 2));
t2.Free;
end.
Output:
PS D:\fpc\git> .\testoutput\treuse.exe
01595A00 0000002A
01595A00 00050001
As you can see the heap manager reused the memory location previously used by the
TTest1 instance for the
TTest2 instance. So there won't be any exception, but you'll get garbage when something continues to use the original reference.
And yes, I'm aware that
FreeAndNil only
Nils the one reference passed to it and doesn't solve everything, but using properties and such one more often then not does not "cache" some reference and then freeing the original reference might point you to the problem more quickly. I personally only use
FreeAndNil when I know that the instance is accessible from somewhere else.
It is interesting, that most languages with garbage collection assume that object instances exist until they go out of scope everywhere. So, what are you supposed to do if you want to replace that instance with another one? I do that often.
I mean, are you going to call Destroy and Create a new one with the same pointer? How do you make sure the other pointers don't keep pointing to the depreciated instance?
Even in FPC that's dubious behaviour to actively abuse.
A TStringlist with objects is a MAP/DICTIONARY pattern as I wrote. So use a map/dictionary. Not the legacy code. That is core.
If one also uses the Name/Value functionality of
TStringList or its encoding mechanisms or the
Load*/
Save*-methods in addition to the object instances then it might be better to use
TStringList.