FreeAndNil is basically and mostly a stop gap for sloppy programming: Use after Free.. But there are some - but rare - scenario's that warrant it.
On a similar token, one might argue that even
Free() can be seen as another helper that promotes sloppy programming.
The actual class destructor is
Destroy(), not
Free(). The only difference between them is that
Free() checks for nil before then calling
Destroy(). But most of the time, in code that manages its pointers
correctly, a pointer will not be
nil when
Free() is called on it, eg:
SL := TStringList.Create;
try
...
finally
SL.Free; // <-- SL is not nil here!
end;
Which is essentially doing this:
SL := TStringList.Create;
try
...
finally
if SL <> nil then SL.Destroy; // <-- nil check is redundant!
end;
The primary reason that
Free() even exists at all is to handle the case where a class' constructor raises an exception (and if you have a constructor that is raising exceptions, you likely have more important issues to worry about). Its destructor still gets called, and needs to free any object members that were already constructed prior to the exception being raised, eg:
constructor TMyClass.Create;
begin
inherited;
Member1 := TSomeClass.Create;
DoSomethingThatMayRaise;
Member2 := TAnotherClass.Create;
end;
destructor TMyClass.Destroy;
begin
if Member1 <> nil then Member1.Destroy; // <-- may be nil or non-nil
if Member2 <> nil then Member2.Destroy; // <-- may be nil or non-nil
inherited;
end;
By exposing
Free() as a wrapper for
Destroy(), it makes writing a destructor a little easier so it doesn't have to know whether the constructor was successful or not, since all class members are zero'ed/nil'ed before the constructor is called.
constructor TMyClass.Create;
begin
inherited;
Member1 := TSomeClass.Create;
DoSomethingThatMayRaise;
Member2 := TAnotherClass.Create;
end;
destructor TMyClass.Destroy;
begin
Member1.Free;
Member2.Free;
inherited;
end;
So, it could be argued that
Free() should only be called on a pointer that has the
possibility of being
nil when its object needs to be destroyed, otherwise
Destroy() should be called directly.
But, the cost of calling
Free() instead of
Destroy() directly is usually negligable...