Well, when someone talks about object creation and destruction, he usually imagine something like this:
However, I want to show that you should avoid “Free” call, whenever possible - by replacing it with call to FreeAndNil, for example:
Please, note, that I mean replacing Free -> FreeAndNil everywhere. I talk not about just using FreeAndNil, when you want to use “Assigned(SomeObj)”, but I really mean everywhere - by do not using Free at all. Yes, including cases with local variables.
Why? The reason is simple: there is no reason for not-doing it. And this post explains why it is so.
When you tell this to common Delphi developer, there are high chances that he’ll vote against it, saying:
1. Free + nil is not full equivalent of FreeAndNil. FreeAndNil clear pointer first and calls destructor second. If your code uses this reference - it will no longer work. For example, if we clear class field, but some internal class uses it during destruction process.
Well, I count it as advantage. It helps to catch bad-design. Because you just described a case, when your code either uses variable instead of Self (i.e. Form1.SomeObj instead of SomeObj) or accesses a partial-constructed object (actually, it is more like partially deleted object now). And by using FreeAndNil you can catch these cases.
Yes, such situations can be made “by-design”, but even so - you will detect an error immediately (obviosly, there will be Access Violation), so you can revert changes back, if you don’t want to redesign your code.
2. So what are examples of righteous/warrantable usage of FreeAndNil?
Hmm, it is not very clear, what example can be provided here. Excluding complicated examples (like in previous block), your code will work the very same way - with or without FreeAndNil. Replacing Free -> FreeAndNil - it is a pure optional action.
You can compare FreeAndNil with seat (safety) belts in cars: if you application runs normally - then FreeAndNil won’t be useful. But if your code mess up with something - then FreeAndNil (as seat belts too) will protect you from consequences. By clearing the reference, FreeAndNil will help you to catch your wrong access immediately. Without it (i.e. by using Free only), your code may continue to run (even without raising an exception) and gave the wrong results or damage global state. It is quite dangerous.
Note, that FreeAndNil is still not enough to fully cover those bad cases - because you can access one object through multiply variables. FreeAndNil will clear one reference, but it won’t touch others. A good example here is all kinds of lists of objects.
3. There is no need for FreeAndNil here! (i.e.: local variables)
Well, local variable can be refactored into global later. FreeAndNil will protect you from misuses (just Free is not). Large amount of code is done by using copy-paste. That is why someone can pick your code, copy it into another place (where variable has different scope or is used multiply times), then he’ll be in trouble - if you didn’t use FreeAndNil (and this “he” may be even you, but few months later). Besides, if you have large routine then you may just not notice, that you have used variable few times (for example - in cycle). By always using FreeAndNil you’ll make your code bullet-proof for modifications.
Moreover, it is just convient, when you have FreeAndNil everywhere, instead of mix of Free/FreeAndNil. And you don’t even need to think about it: “gosh, should I put FreeAndNil here or just Free is enough?!!”.
If you think that FreeAndNil is still overkill in certain case - then why don’t you use Destroy call? Indeed, the Free is overkill in many situations too!
Note, that object’s destructor is not called in 99% of Delphi’s code (a call to procedure is used instead). Then why don’t we take a step further and don’t use FreeAndNil instead? The benefit of using FreeAndNil is far greater then benefit from using Free instead of Destroy. Why? Well, if the first case we got the protection from very tricky mistakes (like I said - it is not panacea, but still a good bonus). And in the second case you got only ability to skip writing additional “if”. Why? Because if you call Destroy for nil-variable, you’ll get an AV immediately (because Self = nil, so any access to object’s field will trigger an undoubted AV). So, there is no problem at all - you will fix the code instantly. Compare this with case, when your code silently produce wrong results!
Furthermore, you can not giveup for a bonus “less writing” by implementing a routine “F”, which simply calls FreeAndNil.
4. Using Free instead of Destroy is recommended by CodeGear! And I never heard of such recommendation for FreeAndNil.
Well, we already examined arguments that Free -> FreeAndNil transition will have more benefits than (already happened) transition Destroy -> Free. Now, don’t you think that argument of not having official approval from CG does look quite dull?
5. Nevertheless, majority of people (who heard about this idea) will continue to claim that this is still overkill.
Why?
The most called reason is the force of the habit: “I write Free automatically, even without thinking”.
Well, actually, this argument is quite serious.
By in this post, I want to show you that there are good reasons to reconsider/change this habbit. Of course, there should be a really good reson for broking your habbits. And here it comes…
6. Safety of your code.
Many people says (and they, indeed, believe so) that experienced programmer can use FreeAndNil only in nessesary places. In all other cases there is no need to clear the object’s reference - so you can use Free.
But that is so wrong: you can not know that.
Imagine, that destructor of your object deletes its object-field by using Free (such common action, right?). The destructor for this sub-object calls a virtual method as part of its destruction process. This method can be empty in base class. Well, so far so good.
Now, someone (or may be you, few months later) will take these classes and override this virtual method. Now he will call another (virtual) method, which belongs to your first mentioned object. And this method (may be also in yet another child class) will try to use already deleted field in your first object. Suppose that this access will run ok (i.e. memory manager do not release this memory yet), but the global state of the application will be damaged forever. Ooops.
FreeAndNil will guard you from this situation (by raising 100% AV), and Free is not.
Why did I mention virtual methods here? That is why you cann’t say: “I know what I am doing - there is no need for FreeAndNil here”. As you can see - you can not know that! Yes, two of your base classes is all good with it, but the very usual code in child classes can lead to the darkest error in the entire class hierarchy. Yes, it is not actually your fault (unless you are also responsible for child classes too), but shouldn’t your code be ideal? And, yes, there is a bug in child class (by calling inadmissible method), but how will you catch it? Without FreeAndNil it is very hard to do.
Okay, even if you still insist on putting FreeAndNil only, when it is nessesary - then we just gave an example, when it is simply not obvious. If you have a habbit of using Free, then you can not even think about considering this case as worth adding FreeAndNil! That is where all problems come. You’ll just put Free call here (as usual) and go somewhere else. And then spend a few days for debugging this tiny, nasty problem later.
By using FreeAndNil everywhere you’ll dispose those problems (should I put it there or not?) - just put it everywhere! Like I said: FreeAndNil - is a safety belts. And realizing it is the only thing left.
Summary: from my point of view - the bonus of protection against fickle/implicit errors outweigh the need to change the habbit.
So.
Why wait? Why don’t use FreeAndNil everywhere, starting today? If you’ll make this your habbit - then you lose nothing, but gain very powerful bonus.
Nevertheless, I suspect that many Delphi programmers, even if they admit this idea, won’t change their practice - just because “someone has said something”. They do not meet with this situation - then it do not exists. So they’ll wait until the bug strike them hard. I’ll repeat it again: FreeAndNil is a safety belt. It protect you from bugs, that not happens yet.
Remark: this long post is just my opinion - feel free to reject it, if you (still) don’t like it.
When you reference an object via more than one reference, you can mistakenly use already disposed object or free the very same object twice. The FreeAndNil gives you protection only if you mistakenly use the object after its dispose, when you have only one reference.
So, even if you use FreeAndNil, you still need an additional checks. And if you have large project and suddenly changes all Free calls to FreeAndNil - then there are good chances then you’ll run into problems. You’ll probably detect many kind of misuses. To track down these problems you’ll need a tool.
Such checks can be found in EurekaLog. We will talk about its memory-related features in more details later (yes, about FastMM too), and now I want to give only brief overview.
Once enabled, this feature can detect a various memory misuse cases and report them through usual exception notification process in EL. Those cases includes double-free and writes to already freed memory. Similar features are very common in advanced memory managers (like FastMM, for example).
It is quite powerful feature, but just don’t forget that EurekaLog is a debugging tool. It is not essential part of Delphi language. You should not write code, which relies on its presence. I.e. you should not forget about FreeAndNil, hoping that EurekaLog will handle all bad cases for you. Besides, there still can be complex (and, actually, rare) situations, when EurekaLog’s features can not catch attempt of invalid memory access (it is not limitation of EL, the same is correct for other tools too). More on this topic later.
Apart from using FreeAndNil and diagnostic features in EurekaLog or MM, you should try to minimize references to each objects and try to use some sort of automatic management. For example, if your object can be included in the list, then it is good to add code, which removes object from any list it belongs to on deletion (there is an example in VCL - it is Components list-property).
Another approach is using interfaces with reference counting. Well, actually, you should be careful there: if you mix manual control of life-time (manual call to destructor) and automatic control (using reference counting in interfaces) - then things will go from bad to worse. But if you use only pure interfaces, then you’ll automatically get rid of such errors: compiler (well, your application at runtime) will look after your objects, not allowing their misuses.
1 comment:
For completeness of your argumentation you should include your observations on performance: Is there a performance penalty of using one approach against the other?
Post a Comment