The Delphi language shares with many others a standard resource allocation pattern to make sure that in case of an exception the resources is properly released. Resources in this context are memory objects, files, operating system objects and handles, and the like. In Delphi, compared to other languages with a garbage collector, the relevance increased by memory management considerations.
Table of Contents
Protecting a Single Object
In the most simple cases, you’d write code like:
allocate resource
try
use resource
finally
free resource
end;
A more specific example would be like:
A1 := TTest.Create;
try
A1.DoSomething;
finally
A1.Free;
end;
So far, so good. Notice that is an error happen during the constructor execution, Delphi will automatically execute the destructor for the partially initialized object (but this could be a topic for another blog post).
Protecting Two Objects: How NOT to Write the Code
The issue I want to focus on is how to write the same type of code if you need to allocate and dispose two resources. Here there are multiple options. What you shouldn’t do (but is fairly common) is to write:
A1 := TTest.Create;
A2 := TTest.Create;
try
A1.DoSomething;
A2.DoSomething (A1);
finally
A2.Free;
A1.Free;
end;
The issue with this code is that in case the creation of A2 fails (and there could be many reasons), the A1 object would remain in memory. Simply pushing the second allocation within the try block is also not good:
A1 := TTest.Create;
try
A2 := TTest.Create;
A1.DoSomething;
A2.DoSomething (a);
finally
A2.Free;
A1.Free;
end;
With this code in case of a failure in object A2 constructor call, the finally block will try to Free an uninitialized object (the default value of a local object reference is undefined). This is why a possible solution is to set A2 to nil at the beginning — as calling Free on a nil object has no effect. Or set all object reference to nil for simplicity and uniformity. Or write two nested try blocks to protect each of the resources.
Protecting Two Objects: A Tale of Three Solutions
This long introduction brings us to the point of this blog post. There are at least 3 different correct solution for the issue of protecting two resources in the same code block, as I just mentioned. Here are the three solutions in an image I “borrowed” from one of our RAD Studio R&D architects, Bruneau Babet.
Provided they are all correct in terms of proper resource management in all scenarios, which are the advantages and disadvantages of these 3 solutions? What is important to consider is that the 2 resources could be 3, or 4 or half a dozen.
Which One to Pick?
The first solution with the nested try blocks fairly clean but more verbose (more lines of code) and has additional nesting that could become an annoyance with multiple resources. Also, there is a runtime cost associated with try blocks, clearly limited but not zero.
The second solution has the least amount of lines of code code and the least amount of runtime cost. The only additional is setting A2 to nil. The code remains readable also with many resources. However, the code is “unbalanced” and it might be slightly confusing.
The third solution goes in the same direction, but it adds one extra technically useless assignment to nil (for A1), offering the advantage of being cleaner and more balanced, and likely more readable after all.
So what’s the best solution? This is really hard to tell,. I personally mostly used #1 in my books, but at Embarcadero we tend to prefer the cleaner and faster ones (that is, #2 or #3) for library code. Interested in your opinions, of course.
Design. Code. Compile. Deploy.
Start Free Trial Upgrade Today
Free Delphi Community Edition Free C++Builder Community Edition
#1 is the more verbose approach, but it is very clear that it is correct.
However, I tend to prefer #3. Although, it seems like unnecessary assignments, it more easily illustrates consistency in assignment, so if someone went on to introduce more class variables, it would be clear as to what to do. I would be less worried about a nil variable assignment not being used (useless as mentioned above), as I would expect the compiler optimiser to recognise this and eliminate the code.