This post was originally published on December 13, 2011. I consider it a classic.
There has been a large and ongoing thread in the Delphi Non-Technical newsgroup about the proper use of FreeAndNil. It’s been contentious and a tad touchy, too — just as I like it. 🙂 The discussion falls out into two schools of thought: Those that use FreeAndNil liberally and almost everywhere, and those that use it not at all or only in very rare, specific cases. The former argues it is “defensive coding”, while the latter argues that it is the coding equivalent of spraying perfume on a skunk.
I am in the latter camp. I am in the latter camp for a very good reason: because the latter camp is right. There’s almost never a reason to use FreeAndNil in the new code that you write.
And I want to be clear about that – I’m talking specifically about new code. If you have old code that was designed in such a way that the scope of your pointers wasn’t tightly contained, then yes, you’ll probably have to use FreeAndNil to make that code work right. But if you are doing that, I hope that you recognize that it is a problem and plan to refactor the code to contain the scope of your pointers. I’m totally aware that legacy code may very well require that you nil pointers because the scope of those pointers is not well managed. I know this because our system has such code, and thus contains calls to FreeAndNil.
So, anyway, here’s an explanation of why I think that FreeAndNil should only be used very, very sparingly.
Before I start, I want to add that this blog post is heavily influenced by the eloquent wisdom and excellent explanations of a number of people who participated in the thread, including Bob Dawson, Wayne Niddery, Rudy Velthuis, Joanna Carter, Mark Edington, and Pieter Zijlstra. Any profundity, excellent examples, pithy similes, or clear descriptions of things are very likely a result of me reading their posts in the thread.
Introduction
FreeAndNil is a function declared in the SysUtils unit and was introduced in Delphi 4, if I recall correctly. I myself suspect that it was added more because of customer demand than because the R&D Team felt some need for it, and I’m reasonably sure that if they had it to do over again, they would not have added it at all. But there it is.
The code for FreeAndNil is as follows:
procedure FreeAndNil(var Obj); var Temp: TObject; begin Temp := TObject(Obj); Pointer(Obj) := nil; Temp.Free; end;
That does seem a bit weird looking – you might expect it to look like this:
procedure FreeAndNil(var Obj: TObject); begin Obj.Free; Obj := nil; end;
But it doesn’t. It looks the way it does for a couple of reasons. First, the parameter passed needs to be a var parameter because two things need to happen. The object referenced needs to be freed, and the reference itself needs to be altered — that is, set to nil. Thus, you need the freedom to change both the reference and the thing being referenced that the var parameter gives. Second, the parameter is untyped because when you pass a var parameter, “Types of actual and formal var parameters must be identical.” Given that, if you declared the parameter as a TObject, then you could pass only a TObject to the method and not any of its descendants.
For instance, the following code will not compile:
program WontWork; {$APPTYPE CONSOLE} uses SysUtils; type TMyClass = class(TObject); procedure Foo(var Obj: TObject); begin WriteLn(Obj.ClassName); end; var MyClass : TMyClass; begin MyClass := TMyClass.Create; try Foo(MyClass); finally MyClass.Free; end; end.
I should point out that the use (or non-use) of FreeAndNil is not an insignificant and uncontroversial issue. The thread that spawned this is typically long. Allen Bauer, the Chief Scientist at Embarcadero, blogged about it, and quite a discussion ensued in the comments – so much so that he felt the need to blog about it again. StackOverflow has a whole bunch of questions on the subject. The VCL uses FreeAndNil in places that I wouldn’t necessarily approve of. I think that in most places its use indicates, uhm, an “older” design choice that probably wouldn’t be made today, given newer language features. In any event, clearly folks have strong views on this and that the use (or note) of FreeAndNil is not “settled science” (though I believe it should be…).
Okay, So When Should You Use FreeAndNil?
In my mind, the answer to the question “When should I use FreeAndNil?” is “never”, or at least “Almost never, and if you must use it, make sure that there is a really, really good reason to do so and that you clearly document that reason”. I myself have never (to my best recollection – I fully expect someone to find some obscure reference to code I wrote years ago that uses it….) used the procedure and see no possible scenario where I would want or need to in the code I write. My recommendation is that you never use it either because I don’t believe that you are writing code that needs it either (unless you are on the Delphi R&D team working in the bowels of the RTL, I suppose).
Why I Don’t Use FreeAndNil and Why You Shouldn’t Either
There are a number of reasons why I don’t use FreeAndNil.
First, a call to Free is sufficient. It gets the job done. Free will, well, free the memory associated with your reference. It does the job completely and totally. Can’t do any more. Setting a pointer to nil doesn’t get you anything. The memory isn’t going to be more free or freed faster as a result of calling FreeAndNil. Since it’s always a good practice to use exactly the right tool and nothing more, there’s no need to make the extra call. Consider this – there’s no SetToZero call for integers, and if there were, why would you use it? All code should be written with “considered intent,” and the indiscriminate use of FreeAndNil shows a lack of consideration and intent.
Second, using FreeAndNil where Free alone will do just fine obfuscates your code. Using a call that executes unneeded instructions sends a message to future readers of the code that shouldn’t be sent. A subsequent developer maintaining your code might look at the call and say “What the heck? Why is FreeAndNil being used here and not just Free? Is something going on here that I don’t know about?” Time might then be wasted investigating, and a satisfactory answer may never be found. Code that uses Free and FreeAndNil as exactly the same thing has reduced the amount of information that your code can convey. And when you are dealing with something as important as memory management, you certainly don’t want to reduce the amount of information your code can convey.
FreeAndNil has a clear meaning – it is a very clear indicator that the pointer being freed has meaning outside of the scope where it is used. If it doesn’t say that, then you shouldn’t use it. If you use FreeAndNil when that is not the case, then you’ve sent a bad message to future maintainers. Clarity in code is paramount – nothing should be done to decrease that clarity. Code should be intentional and there for a reason. Code that is there that doesn’t need to be can be misleading and distracting. Misleading and distracting are not two thoughts that developers want crossing their minds while maintaining code.
Free has meaning as well – it clearly states that the use of that pointer reference is now done and over with. As noted above, there’s no need to call anything else. The indiscriminate use of FreeAndNil fails to draw the clear distinction between Free and FreeAndNil. Losing clarity in your code is bad, right?
Third, one of the justifications for using FreeAndNil is that it is defensive and that it protects against using a pointer at the wrong time. The claim is that if a pointer is nil, and you then use that pointer, then you’ll know right away, and the bug would be easy to find. Thus, if you feel the need to use FreeAndNil to ensure that you don’t misuse a pointer somewhere, then it is very likely you have a design problem: the scope of the pointer in question is larger than the use of that pointer in your code. Or, in other words, the scope of a pointer and the scope of the use of that pointer aren’t the same and they should be. If they aren’t , you are simply begging for trouble.
If you want to really be persnickety, a variable that is broader in scope than it’s use is a form of a global variable. And I would hope that we agree that global variables are bad. If we can’t agree on that, well, then we can’t agree on anything.
Maintaining proper scope is critical to good, clean code. I’ve discussed this before, and so I won’t go on about it here. The germane point here is that if the scope of a pointer is of the “laying around waiting to be used”, then there is no limit to the mischief that this wayward pointer can cause. So, if you don’t “leave a pointer lying around”, you can’t misuse it. So, well, don’t leave a pointer lying around. If you don’t leave roller skates at the bottom of the stairs, you can’t go careening down the hallway. Keep your pointers and the use of those pointers in the same scope and you can’t misuse a pointer. And you won’t feel the need to use FreeAndNil.
And if you do use it for defensive reasons, you have to use it everywhere. You have to use it in ever single place it is needed and you can’t miss a single place. And, every single maintainer of the code after you has to as well. One instance of not using it basically removes all the reasons for using it. It’s a much better plan to simply control your scope and never feel the need for it.
So, in the end…
In the end, I guess the argument for using FreeAndNil seems to boil down to:
“Of course I use FreeAndNil – it protects against bugs and makes other bugs easy to find, and besides, what’s the harm?”
Well, it would seem that none of those reasons is really true. The real argument is:
“If your code requires you to use FreeAndNil to reveal and easily find bugs, then your design is wrong. Good, clean code never feels the need to worry about errant pointers.”
Hey, look: design your code however you like. However, if you were to ask me, I’d say to design your code in such a way that FreeAndNil sends no signal, doesn’t find any bugs any sooner, doesn’t protect against anything, and thus becomes utterly superfluous.