在哪里释放动态分配的TFrame组件的对象?

use*_*198 6 delphi destructor tframe

我有一个包含的表格TFrame.该TFrame包含ComboBox被动态填充.每个ComboBox条目都有一个关联的对象.当TFrame被调用的析构函数被调用时,ComboBox已经清除了中的Items 而没有释放它们的相关对象.无论我是ComboBox在设计器视图中删除表单,还是在代码中使用nil或TFrame作为其所有者动态创建它,都会发生这种情况.我目前使用contains的OnDestroy事件TForm来调用包含的清理过程TFrame.

是否有更好的方法不需要由TFrame容器进行显式过程调用?理想情况下,动态添加的对象应该ComboBox被释放?

Ser*_*yuz 8

你说当调用TFrame的析构函数时,ComboBox的Items已经被清除了.事实并非如此,ComboBox项目永远不会被清除.当ComboBox销毁Items时,它们的计数仅为0.

当您退出应用程序并且VCL销毁包含框架和ComboBox的表单时,操作系统也会销毁本机ComboBox控件,因为它被放置在被销毁的窗口中.当您以后访问项目以便能够在帧析构函数中释放对象时,VCL必须重新创建一个本机ComboBox控件,其项目计数为0.

我建议的解决方案很简单.不要将框架释放到框架中,而是在框架中摧毁框架OnDestroy.那将是在表单的底层窗口被销毁之前,因此您将能够访问您的对象.

表格单位

procedure TMyForm.FormDestroy(Sender: TObject);
begin
  MyFrame.Free;
end;
Run Code Online (Sandbox Code Playgroud)

框架单元

destructor TMyFrame.Destroy;
var
  i: Integer;
begin
  for i := 0 to ComboBox1.Items.Count - 1 do
    ComboBox1.Items.Objects[i].Free;
  inherited;
end;
Run Code Online (Sandbox Code Playgroud)


kob*_*bik 6

你可以像这样使用TFrame's WM_DESTROY处理程序:

unit Unit2;

interface

uses
  Windows, Messages, SysUtils, Classes, Controls, Forms, StdCtrls;

type
  TFrame1 = class(TFrame)
    ComboBox1: TComboBox;
  private
    procedure WMDestroy(var Msg: TWMDestroy); message WM_DESTROY;
    procedure FreeComboBoxItems;
  public
    constructor Create(AOwner: TComponent); override;
  end;

implementation

{$R *.dfm}

constructor TFrame1.Create(AOwner: TComponent);
begin
  inherited;
  // Add some object items to the ComboBox
  ComboBox1.AddItem('a', TButton.Create(nil));
  ComboBox1.AddItem('b', TMemoryStream.Create);
  ComboBox1.AddItem('c', TList.Create);
end;

procedure TFrame1.WMDestroy(var Msg: TWMDestroy);
begin
  // Make sure the TFrame is actually destroying - not recreated
  if (csDestroying in ComponentState) then
    FreeComboBoxItems;
  inherited;
end;

procedure TFrame1.FreeComboBoxItems;
var
  I: Integer;
begin
  OutputDebugString('TFrame1.FreeComboBoxItems');
  with Self.ComboBox1 do
    for I := 0 to Items.Count - 1 do
    begin
      OutputDebugString(PChar(Items.Objects[I].ClassName + '.Free'));
      Items.Objects[I].Free;
    end;
end;

end.
Run Code Online (Sandbox Code Playgroud)

另一种选择是为整个应用程序创建一个Base祖先TAppBaseForm类和一个TAppBaseFrame,并将所有Form派生为TAppBaseForm和所有Frames一样TAppBaseFrame.通过这种方式,TAppBaseForm可以通知所有孩子TAppBaseFrame,在TAppBaseForm.FormDestroy事件处理程序中销毁所有者Form .此时,ComboBox项目仍然有效(如Sertac Akyuz的回答所述).


NGL*_*GLN 4

你的问题并不是很有用,因为 - 一般来说 - 不鼓励存储在 GUI 控件中另请参阅大卫关于如何更改设计的评论。

\n\n

不过,使这个问题变得有趣的原因是组合框直接作为表单的子项与作为表单的另一个子项(在本例中为您的框架)的子项之间的区别。显然,组合框项目在调用该框架的析构函数之前被销毁。那么显而易见的替代方案是: override Frame.BeforeDestruction、 override Frame.DestroyWindowHandle、 overridingFrame.DestroyWnd或 catch WM_DESTROYin an overriddenFrame.WndProc,但在项目已经消失之前,它们都不会被调用。

\n\n

接下来要尝试的是对组合框重复此操作。事实证明,当WM_DESTROY到达组合框时,这些项目仍然在那里。然而,当控件真正被销毁时,请注意捕获该消息 \xc3\xb3,因为 VCL 可能会频繁地重新创建组合框。使用插入类来实现它TComboBox,如下所示:

\n\n
unit Unit2;\n\ninterface\n\nuses\n  Windows, Messages, Classes, Controls, Forms, StdCtrls;\n\ntype\n  TComboBox = class(StdCtrls.TComboBox)\n  protected\n    procedure WndProc(var Message: TMessage); override;\n  end;\n\n  TFrame1 = class(TFrame)\n    ComboBox1: TComboBox;\n  end;\n\nimplementation\n\n{$R *.dfm}\n\n{ TComboBox }\n\nprocedure TComboBox.WndProc(var Message: TMessage);\nvar\n  I: Integer;\nbegin\n  if (Message.Msg = WM_DESTROY) and (csDestroying in ComponentState) then\n    for I := 0 to Items.Count - 1 do\n      Items.Objects[I].Free;\n  inherited WndProc(Message);\nend;\n\nend.\n
Run Code Online (Sandbox Code Playgroud)\n\n

现在,回答您的问题:“这是更好的方法吗?”

\n\n

是的,因为它提供了在框架级别上对象被破坏的保证。换句话说:你不必记住处理每个实例。

\n\n

不,不是,因为此解决方案要求在任何情况下都允许释放组合框中的对象,从而将使用限制在不必要的额外边界。

\n\n

那么,这个答案有用吗?好吧,如果它阻止您使用当前的方法,那么它就是。

\n\n
\n\n

Parent此外,我还找到了另一种选择,在包含的表单中将框架的属性设置为 nilOnDestroy

\n\n
procedure TForm2.FormDestroy(Sender: TObject);\nbegin\n  Frame1.Parent := nil;\nend;\n
Run Code Online (Sandbox Code Playgroud)\n\n

在这种情况下,您可以在框架的析构函数中安全地销毁存储在组合框中的对象。但这个解决方案比您当前的解决方案更糟糕,因为它不是描述性的。那么就Frame1.FreeComboObjects好多了。

\n