德尔福所有权混乱

时间:2011-08-16 08:43:30

标签: delphi memory-management components delphi-xe ownership

我一直认为所有者负责销毁视觉控制,如果我将nil作为所有者传递,我可以手动控制销毁。

考虑以下示例:

TMyForm = class (TForm)
private
  FButton : TButton;
end;

...
FButton := TButton.Create(nil);   // no owner!!
FButton.Parent := Self;

我希望这个按钮产生内存泄漏,但它没有,实际上调用了TButton的析构函数。

进一步调查显示,TWinControl析构函数包含以下代码片段:

I := ControlCount;
while I <> 0 do
begin
  Instance := Controls[I - 1];
  Remove(Instance);
  Instance.Destroy;
  I := ControlCount;
end;

看起来它正在破坏子组件(Parent设置为控件本身的组件。)

我没想到父控件会破坏控件。任何人都可以解释为什么会这样吗?如果我传给一个拥有者,谁会摧毁这个物体呢?

2 个答案:

答案 0 :(得分:13)

  

为什么会这样?

这是有道理的,它是设计的。当父母被摧毁时,您认为孤儿控制会发生什么?它们应该突然开始作为顶级窗户漂浮吗?可能不是。他们应该重新成为另一个控制的父母吗?哪一个?

who is destroying the object if I pass in an owner?

Parent,如果已分配并首先被释放。 TWinControl覆盖TComponent的析构函数以释放其子控件 first (继承的析构函数仅在稍后调用)。子控制notify他们的Owner被销毁,将其从拥有的组件列表中删除。这就是为什么所有者不会在以后的析构函数中再次尝试释放你的对象。

如果ParentOwner的对象相同,则上述情况也适用。

如果ParentOwner是两个不同的对象,并且您首先释放所有者,那么所有者组件将释放其拥有的所有组件(请参阅TComponent's destructor)。您的对象是TControl后代,TControl会覆盖析构函数以调用SetParent(nil);,它会从父级的子控件列表中删除该实例。这就是为什么父母不会在以后的析构函数中再次尝试释放你的对象的原因。

答案 1 :(得分:8)

我现在访问的最早版本是Delphi 5,TWinControl析构函数也包含你在那里发布的代码,所以这种行为已经存在了很长时间。当你考虑它时,它是有道理的 - Controls视觉组件,当你销毁它们的容器(Parent)时,那么破坏孩子也是有意义的。 TWinComponent的析构函数不能告诉你如何处理它们(隐藏它们?将它们重新显示为Parent.Parent?但是如果当前Parent是顶级窗口,即它没有Parent?等等)。因此,VCL的设计者们认为这是最安全的选择,避免内存/手柄泄漏(特别是在早期处于优势的胜利处理,因此避免泄漏它们可能是首要任务)。因此,如果您希望孩子留下来,您应该在销毁容器之前重新让他们成为父母。

顺便说一句。如果您通过所有者,则TComponent.DestroyComponents;(由TComponent.Destroy调用)会破坏该组件。