Delphi 德尔福所有权混乱

Delphi 德尔福所有权混乱,delphi,memory-management,components,delphi-xe,ownership,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析构函数包含

我一直认为所有者有责任销毁视觉控件,如果我作为所有者通过
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;
I:=ControlCount;
而我呢
开始
实例:=控件[I-1];
移除(实例);
例如,摧毁;
I:=控制计数;
结束;
看起来它正在销毁子组件(父组件设置为控件本身的组件)


我不希望父控件破坏该控件。有人能解释为什么会这样吗?如果我交给所有者,谁在销毁该对象?

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


顺便说一句,如果您传递了所有者,则
TComponent.DestroyComponents
(由
t组件调用。Destroy
)销毁该组件

为什么会这样

这是有道理的,也是经过设计的。当父控件被销毁时,您认为孤儿控件会发生什么情况?它们是否应该突然开始像顶级窗户一样四处飘浮?可能不会。是否应该将它们重新指定给另一个控件?哪一个

who is destroying the object if I pass in an owner?
父级
,如果已分配并首先被释放
TWinControl
首先重写
t组件的析构函数以释放其子控件(继承的析构函数仅在以后调用)。子组件控制其
所有者
关于被销毁的信息,从而将其从其拥有的组件列表中删除。这就是为什么所有者不会在稍后的析构函数中再次尝试释放对象

如果
Parent
是与
Owner
相同的对象,则上述内容也适用


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

TComponent.DestroyComponents
(从析构函数调用)在不设置父对象而设置所有者时进行销毁。我从来没有注意到在
TWinControl
中也会发生破坏,这很好,在这个问题上有点类似的困惑。在的“注释”中对其进行了解释,但我发现这一解释有点令人困惑(关于流媒体部分)…如果我传入所有者,是什么阻止了
Destroy
被调用两次?在这种情况下,两个析构函数都应该执行…对子对象的引用将从内部列表中删除,因此第二个析构函数不会“看到”(已销毁)子对象。但是:
t组件
使用的不是
控件
数组(包含子对象),而是
组件
列表,VCL中有通知系统,必须考虑所有不同的场景,以便及时更新内部所有权列表。即t组件的析构函数调用
,如果FOwner为nil,则调用FOwner.RemoveComponent(Self)ain:你能解释得更详细一点吗(也许在你的答案的编辑中)?我看不出TWinControl的析构函数从所有者的组件列表中删除控件的位置…+1谢谢!非常好的解释,尽管我会发现如果只对子控件执行
SetParent(nil)
,它会更有说服力。我的意思是,整个组件所有权原则是为对象销毁而构建的,因为
TWinControl
继承了
TComponent
我不明白为什么需要第二种机制。这只会使破坏过程更加复杂。或者你知道一个例子,除了组件所有权之外,还需要这个吗?哦,我想我在第一部分已经解释过了。“孤儿控制怎么办?”因为父母有责任清理孩子,就像所有权有责任清理拥有的组件一样。记住,父母和主人可能是两个不同的对象。我可能会监督一些事情,但我不明白重点。具有
父属性
的每个对象也具有
所有者
属性。那么,为什么
parent
要处理对象销毁呢?为什么不使用现有的销毁所有权机制?“孤儿控件应该发生什么”:为什么不将
父控件设置为
nil
!我只是说,每一个这样的对象都有可能指定一个所有者,所以我不认为有必要添加