How should you safely create and free multiple objects?
Basically, this sort of thing:
newOrderSource := TWebNewOrderSource.Create();
twData := T
As I'm sure everyone knows, the standard way to manage an object is like this:
A := TMyObject.Create;
try
A.DoSomething;
finally
A.Free;
end;
If there is an exception in TMyObject.Create
then the destructor will be called and then the exception raised. In that case A
will not be assigned to.
When you have multiple objects you can repeat the pattern:
A := TMyObject.Create;
try
B := TMyObject.Create;
try
A.DoSomething;
B.DoSomething;
finally
B.Free;
end;
finally
A.Free;
end;
This very quickly becomes a mess and hence the question.
A standard trick is to take advantage of the fact that Free
can safely be called on a nil
object reference.
A := nil;
B := nil;
try
A := TMyObject.Create;
B := TMyObject.Create;
A.DoSomething;
B.DoSomething;
finally
B.Free;
A.Free;
end;
This does have the minor weakness that it is not resilient to exceptions being raised in B.Free
but it is not unreasonable to regard this as a failure condition that can be ignored. Destructors should not raise exceptions. If they do then your system is probably broken irredeemably.
This pattern above can become a little messy as more objects are added so I personally use the following helper methods.
procedure InitialiseNil(var Obj1); overload;
procedure InitialiseNil(var Obj1, Obj2); overload;
procedure InitialiseNil(var Obj1, Obj2, Obj3); overload;
procedure FreeAndNil(var Obj1); overload;
procedure FreeAndNil(var Obj1, Obj2); overload;
procedure FreeAndNil(var Obj1, Obj2, Obj3); overload;
In fact my code has versions with even more parameters. For ease of maintenance this code is all automatically generated from a short Python script.
These methods are implemented in the obvious way, e.g.
procedure FreeAndNil(var Obj1, Obj2);
var
Temp1, Temp2: TObject;
begin
Temp1 := TObject(Obj1);
Temp2 := TObject(Obj2);
Pointer(Obj1) := nil;
Pointer(Obj2) := nil;
Temp1.Free;
Temp2.Free;
end;
This allows us to re-write the sample above like this:
InitialiseNil(A, B);
try
A := TMyObject.Create;
B := TMyObject.Create;
A.DoSomething;
B.DoSomething;
finally
FreeAndNil(B, A);
end;