25

我一直认为,销毁视觉控件是由所有者负责的,如果我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设置为控件本身的组件)。

我没想到父控件会破坏控件。谁能解释为什么会这样?如果我传入所有者,谁会破坏该对象?

4

2 回答 2

13

为什么会这样?

这是有道理的,而且是设计使然。您认为当父级被销毁时,孤立的子级控件应该发生什么?他们是否应该突然开始作为顶级窗口浮动?可能不是。是否应该将它们重新设置为另一个控件?哪一个?

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

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

If Parentis the same object as Ownerthen 以上也适用。

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

于 2011-08-16T11:01:10.667 回答
8

我现在可以访问的最早版本是 Delphi 5,TWinControl析构函数也有你在那里发布的代码,所以这种行为已经存在了很长时间。当你考虑它时,它是有道理的 -Controls视觉的组件,并且当您销毁它们的容器(父级)时,也有必要销毁子级。TWinComponent 的析构函数无法为您决定如何处理它们(隐藏它们?将它们重新设置为 Parent.Parent?但是如果当前 Parent 是顶级窗口,即它没有 Parent?等等)。因此,VCL 的设计者决定这是最安全的选择,避免内存/句柄泄漏(尤其是早期的 win 句柄,因此避免泄漏它们可能是重中之重)。所以如果你想让孩子留下来,你应该在销毁容器之前重新养育他们。

顺便提一句。如果你传递一个 Owner ,那么TComponent.DestroyComponents;(由 调用TComponent.Destroy)会破坏该组件。

于 2011-08-16T09:10:13.343 回答