One of the most used object-oriented patterns is probably this one:
foo := TFoo.create;
try
//do something with foo
finally
foo.free;
end;
It basically means, that if you create an instance of some class and if there is no “owner” who takes care of the new instance’s life cycle, then it is your responsibility to destroy the instance again, as soon as you are done with it.
If you don’t care about freeing the objects you created, then you will get “memory leaks”, i.e. you app will use (possibly a lot) more memory as it should.
The technique above is clear and simple, but what if you have more than one object that is needed in a routine? Line them up?
foo := TFoo.create;
bar := TBar.create;
try
//do something with foo and bar
finally
foo.free;
bar.free;
end;
Looks good, and I see this pattern frequently – but hold on! What happens if there is an exception when TBar is created? Right, foo won’t be destroyed in that case. So maybe handle it like this then:
foo := TFoo.create;
try
bar := TBar.create;
try
//do something with foo and bar
finally
bar.free;
end;
finally
foo.free;
end;
In theory this would be the correct way, and there are a lot of examples that work like that. But look at the code – right: looks really ugly, esp. if we extend that to even more objects being created. Apart from readability there is also some performance hit: every try-frame needs some extra CPU cycles, which may sum up if your routine is time critical already.
My suggestion is this one:
foo := nil;
bar := nil;
try
foo := TFoo.create;
bar := TBar.create;
//do something with foo and bar
finally
bar.free;
foo.free;
end;
Now lets discuss some questions about this pattern:
Didn’t we learn, that calls to “Create” have to be made outside the try-finally block?
No, not really. In fact it doesn’t matter where you create an instance of a class. The first example (which is mentioned in many articles) only suggests this, because it’s easy to do so with just one object.
If TBar.create fails, what happens with its bar.free counter part? Wouldn’t it actually be executed on some not “really created” object?
No, in fact bar.free is special method, which can safely be called even on variables pointing to NIL. Free checks if self<>nil and only then the actual destructor is called.
Why did you initialize fo and bar with NIL?
Because otherwise they might have random values. Esp. if they are declared as local variables. And we need NIL initialized variables so that “free” does not call random code in the case of an exception in one of the create calls.
If TBar.create fails with an exception, wouldn’t be there the chance that some address value would have been written to bar already – so that bar.free would be executed on some half initialized instance?
No, if TBar.Create fails with an exception, then the class function “create” would (like any other function) return NO value at all. bar would keep its original value that is.