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
!我只是说,每一个这样的对象都有可能指定一个所有者,所以我不认为有必要添加