如大家所知,管理对象的标准方法如下:
A := TMyObject.Create;
try
A.DoSomething;
finally
A.Free;
end;
如果在TMyObject.Create
中出现异常,则会调用析构函数,然后抛出异常。在这种情况下,A
将不被分配。
当您有多个对象时,可以重复此模式:
A := TMyObject.Create;
try
B := TMyObject.Create;
try
A.DoSomething;
B.DoSomething;
finally
B.Free;
end;
finally
A.Free;
end;
这很快就会变得一团糟,因此才有了这个问题。
一个常见的技巧是利用这样一个事实:Free
可以安全地在 nil
对象引用上调用。
A := nil;
B := nil;
try
A := TMyObject.Create;
B := TMyObject.Create;
A.DoSomething;
B.DoSomething;
finally
B.Free;
A.Free;
end;
这种方式的小缺陷是,它对于在B.Free
中抛出异常不具有弹性,但是将其视为可以忽略的失败条件并非不合理。析构函数不应该引发异常。如果他们这样做了, 那么您的系统可能无法挽回地损坏。
随着添加更多对象,上述模式可能会变得有点混乱,因此我个人使用以下辅助方法。
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;
实际上,我的代码有更多参数的版本。为了方便维护,这段代码全都是从一个简短的Python脚本自动生成的。
这些方法的实现方式很显然,例如:
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;
这使我们可以像这样重写上面的示例:
InitialiseNil(A, B);
try
A := TMyObject.Create;
B := TMyObject.Create;
A.DoSomething;
B.DoSomething;
finally
FreeAndNil(B, A);
end;
newOrderSource := TWebNewOrderSource.Create();
放在try之前,并丢弃newOrderSource := nil;
。 - Gerry Collnil
,因为 Delphi 将所有内存初始化为零,这恰好与 nil 相同。这就是为什么您不需要使用if Assigned()
保护每个 Free 调用的原因。这也是为什么要调用 Free 而不是 Destroy 的原因。 - David HeffernanFree
不是一个虚方法,因此编译器会生成一个普通的函数调用,并将Self
作为参数传递。这就像将nil
作为接受TObject的过程的第一个参数一样,只是看起来有点奇怪。 - chuckj