德尔福所有权混乱

jpf*_*ius 24 delphi components memory-management ownership delphi-xe

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

请考虑以下示例:

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

...
FButton := TButton.Create(nil);   // no owner!!
FButton.Parent := Self;
Run Code Online (Sandbox Code Playgroud)

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

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

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

看起来它正在摧毁子组件(Parent设置为控件本身的组件).

我没想到父控件会破坏控件.任何人都可以解释为什么会这样吗?如果我通过一个所有者,谁在摧毁这个物体?

Ond*_*lle 13

为什么会这样?

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

who is destroying the object if I pass in an owner?
Run Code Online (Sandbox Code Playgroud)

Parent,如果它被分配并首先被释放.TWinControl覆盖TComponent的析构函数来释放其子控件第一(继承析构函数只是后来被称为).子控件通知它们Owner被销毁,将其从拥有的组件列表中删除.这就是为什么所有者不会在以后的析构函数中再次尝试释放您的对象.

如果Parent是同一个对象,Owner则上述情况也适用.

如果ParentOwner是两个不同的对象,你先自由的所有者,那么所有者成分释放其拥有的所有部件(见TComponent的析构函数).您的对象是TControl后代并TControl覆盖要调用的析构函数,该析构函数SetParent(nil);从父控件的子控件列表中删除该实例.这就是为什么父母不会在以后的析构函数中再次尝试释放你的对象的原因.

  • 哦,我以为我在第一部分解释了它."孤儿控制怎么办?" 由于父母关系负责清理孩子,因为所有权承担了清理自有组件的责任.请记住,父母和所有者可能是两个不同的对象. (2认同)
  • 我可能会监督一些事情,但我不明白这一点.具有`parent`属性的每个对象也具有`owner`属性.那么为什么`parent`会处理对象破坏呢?为什么不使用现有的所有权机制进行销毁?"孤儿控制应该发生什么":为什么不把`Parent`设置为`nil`? (2认同)

ain*_*ain 8

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

BTW.如果您通过所有者然后TComponent.DestroyComponents;(被叫TComponent.Destroy)破坏该组件.

  • VCL中有通知系统,必须处理所有不同的方案,以便及时更新内部所有权列表.即TComponent的析构函数调用`if FOwner <> nil然后FOwner.RemoveComponent(Self);` (2认同)