Back in late 1992 or 1993 or so, we had a dilemma. We wanted to add exceptions to the Turbo Pascal (what was soon to become the basis for Delphi’s programming language). WindowsNT was under full-swing development. With this new OS came a new-fangled thingy called Structured Exception Handling (SEH). So what was the dilemma here? If you’ll recall, the first release of Delphi was targeting Windows 3.1, aka. Win16. There was no OS-level support for SEH. Also, WindowsNT wasn’t going to be released as a mass-market consumer-based OS. Again, what’s the problem? Just add your own implementation of SEH to the language and move on. The problem was all about "safety." So we, of course, added our own specific implementation of SEH to the 16bit version of the Delphi language. Aren’t exceptions suppose to make your code more safe? Ok, I’m being obtuse and a little evasive here.
The fundemental problem we faced was the notion of a partially constructed object. What if halfway through an object’s constructor an exception was raised? How do you know how far into the execution of the constructor you got by the time the exception handler is executed and the object’s destructor is called? The constructor is excuting merrily along, initializing fields, constructing other objects, allocating memory buffers, etc… Suddenly, BAM! One of those operations fail (can’t open a file, bad pointer passed in as a constructor parameter, etc…). Since the compiler had already injected an implicit exception handler around the execution of the constructor, it catches the exception and promptly and dutifully calls the destructor. Once that is complete and the partially constructed object is destroyed and all the resources are freed, the exception is allowed to continue, in other words, is re-raised. The problem in this scenario is the fact that the destructor really has absolutely no clue why it got called (sure it could see that an exception is currently in play, but so what?). The destructor doesn’t know if the instance was ever fully constructed and if not, how much got constructed.
The solution turned out to be remarkably simple and somewhat clever at the same time. Since all classes in Delphi have an associated virtual method table (VMT) each instance must be initialized to point to that table. Since Delphi classes allow you to make virtual method calls from within the constructor, that VMT pointer has to be initialized before the constructor is allowed to execute. If the VMT pointer has to be set before the constructor executes, why not just initialize the entire instance to some known state? This is exactly what is done. The entire instance is initialized to zero (0), the VMT pointer is set, if the object implements interfaces, those VMT pointers are also set. Because once the user’s code in the object constructor begins to execute you know that the instance data is in a known state. By using this fact, the destructor can easily tell how far in the object’s initialization sequence things got before the world went haywire. Remember yesterday’s post where I mentioned the FreeAndNil procedure? Another item to note is the non-virtual TObject.Free method. Because you can assume that if an instance field contains a non-nil or non-zero value, it must have been successfully initialized, it should also be OK to de-initialize it. This is more important for any memory allocations or object constructions that took place in the constructor (or any other object method for that matter). The destructor has to know when a valid pointer is in that field. So a non-nil value means, go ahead and free the memory or destroy the object.
We realized too, that it would be very tedious and error-prone for the user to always have to remember to always do this pattern: if FField <> nil then FField.Destroy; Enter TObject.Free. If you opened System.pas and looked at the implementation of Free, it simply does if Self <> nil then Destroy; So you can safely call the Free on a nil, or unassigned instance. That is because the check is done within that method. All you need to do is FField.Free; and your destructor is now "exception safe." The same thing can be done for memory allocated with GetMem. You can safely call FreeMem(FField), even if FField is nil. It just returns. Finally, it should be noted that certain "managed types" such as strings, interfaces, dynamic arrays and variants are also automatically handled through some compiler generated meta-data. So just before an object instance’s memory is freed, an RTL function is called that will take the instance and this meta-data table which contains field types and offsets so this RTL function knows how to free certain instance fields. Again, if a particular field is nil, it is simply passed over with no action needed.
What about the FreeAndNil thingy? For the astute among you, you’ve probably noticed that the implementation of that procedure actually sets the passed in reference to nil first and then destroys the instance. Shouldn’t the name actually be NilAndFree? Yeah, probably. But it just doesn’t roll of the tougue very well and is equally confusing. "So if you set the referene to nil first… how can you destroy it?" So why was it implemented in this manner? Exception safety is big one reason. Another significantly more obscure reason involves intimately linked objects. Suppose you have one object that holds a list of other objects which in-turn hold a reference back to the "owner" object? Depending on the order in which the objects get destroyed, they may need to notify other objects of their impending doom. Since the owner and the owned objects are intimately linked, they directly call methods on each other throughout their lifetime. However, during destruction, it could be very dangerous to willy-nilly call methods on the other instance while it is in the throws of death. By setting the instance pointer to nil before destroying the object, a simple nil-check can be employed to make sure no method calls are made while the other instance is being destroyed.
So there you have it; a few little tips on ensuring your objects are "exception safe" and a couple of hints into when you should use FreeAndNil. By peeking under the hood and examining the code, you can get a better understanding of why and how things are implemented. So, you could always use the if FField <> nil then FField.Destroy pattern buy why when calling FField.Free; does all the work for you? Using the pattern, if FField <> nil then FField.Free; is a redundant, as is, if Assigned(FField) then FField.Free;Posted by Allen Bauer on November 1st, 2006 under Uncategorized |