开发者

How to safely Create and Free multiple objects in Delphi

开发者 https://www.devze.com 2023-04-10 17:10 出处:网络
How should you safely create and free multiple objects? Basically, this sort of thing: newOrderSource := TWebNewOrderSource.Create();

How should you safely create and free multiple objects?

Basically, this sort of thing:

  newOrderSource := TWebNewOrderSource.Create();
  twData := TTWData.Create();
  webData := TWebData.C开发者_运维技巧reate();

  try
    //do stuff
  finally
    newOrderSource.Free();
    twData.Free();
    webData.Free();
  end;

In this case, the second and third create commands aren't safe, as they work with a database. Should I just put all the Creates in the try block and check if they are assigned before I call free on them?


You can do this with one try block if you assign nil to the variables first like,

newOrderSource := nil;
twData := nil;
webData := nil;
try
  newOrderSource := TWebNewOrderSource.Create();    
  twData := TTWData.Create();    
  webData := TWebData.Create();    

  //do stuff    
finally    
  webData.Free();    
  twData.Free();    
  newOrderSource.Free();    
end;    

This works because Free() checks Self for nil.


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;
0

精彩评论

暂无评论...
验证码 换一张
取 消