yoy.be "Why-o-Why"

My take on Free vs FreeAndNil

2022-06-25 00:52  FreeAndNil  coding delphi  [permalink]

Coding in Delphi and beyond: Delphi Debates: FreeAndNil

;TLDR: I agree.

I, for myself, have a pretty straight-forward rule to follow. In most cases 'lifetime management' of objects is pretty plain and normal: there's one single place in the code where an object comes into existence (the call to a constructor), and exactly one single place in the code where it is cleaned up. In that case there's no reason that I can think of to use anything else than the Free method. Sometimes the constructor is called from another constructor, and you free from the partner destructor, in effect linking the lifetime of the object to this 'owner' object. Most other cases, you just need an instance of a class to do something with. It typically looks like this:

var
a:TThing;
begin
a:=TThing.Create;
try
//...
finally
a.Free;
end;
end;

Neat! One very (very) important thing to note here is that under no circumstance you're supposed to do anything with this object reference after that (other than assigning with a new constructor call etc.) Thanks to Delphi internals, you'll probably get an access violation, but you might just as well end up calling some code that has been put in the memory where the instance was, and has unintended consequences.

If, for any reason whatsoever, you need/want to write code that may or may not be assigned to a live instance of an object, you could and should use nil. In the places in the code where it's not sure if there's an instance, be sure to check for a nil value. Consider this snippet:

var
a:TThing;
begin
a:=nil;//counter warning
try
if //only in some cases
begin
a:=TThing.Create;
//...
end;
//...
if a<>nil then
begin
//use a for something
end;
//...
finally
if a<>nil then
begin
a.Free;
a:=nil;
end;
end;
end;

Seasoned Pascal-coders will have objections to this snippet, rightfully so, but the point I want to make that there's some extra steps taken here to 'keep it safe'. There's just this one thing that's not so easy to spot: a.Free; could in theory throw an exception. If there's an except clause below that, and it wants to get some data from a, it won't work. a isn't nil, but the constructor may have already taken down the object instance so far down it will no longer behave as expected. There's a way around that though, and you can see for yourself rightaway if you have a look for yourself at FreeAndNil itself:

procedure FreeAndNil(var Obj);
var
  Temp:TObject;
begin
  Temp:=TObject(Obj);
  Pointer(Obj):=nil;
  Temp.Free;
end;

(There's some casting to/from pointer and TObject here, but that's to make sure it works in even the strangest cases. It basically sets the reference to nil before calling Free.) If you've seen that first without the context I described above, you might think 'what is going on here' or 'what is the fuss about'. So I hope I was able to bring some enlightenment. The best case would be if you've learnt nothing new here, and also agree with this rule:

If you have an abnormal object instance lifetime management situation and/or need to entertain instance references that may be nil, use FreeAndNil. In all other cases use plain Free (and keep calls to constructors and destructors in perfect balance!)

twitter reddit linkedin facebook