【问题标题】:How to Free objects not owned by the form如何释放不属于表单的对象
【发布时间】:2019-09-22 13:20:13
【问题描述】:

我想知道在释放不属于表单的对象时什么是最佳做法,什么不应该、应该或必须做。 我是否应该覆盖表单的析构函数并释放其中的对象 或者 我应该使用 FormClose 事件并释放其中的对象吗

感谢任何 cmets,也许还有第三种更好的方法,我不知道。

已经在 Google 上看过了,但是要么没有相关的 cmets,要么我的搜索问题有误。

目前我有:

procedure TmyForm.FormClose(Sender: TObject;
  var Action: TCloseAction);
begin
  Registry.Free;
  FileStream.Free;
  MemStream.Free;
end;

这行得通,只是想知道这是否是正确的方法。

【问题讨论】:

  • 默认 ActioncaHide,所以不,这不是正确的做法。
  • 释放 onClose 事件处理程序中的对象是正确的地方,如果表单只使用 ShowModal 显示。
  • @nolas - 不引起任何问题不会使它正确。您不想跟踪不必要的细节,我会不会显示此表单非模态等。从语义上讲,正确的做法是释放与您创建它的位置匹配的对象。只有在 OnOpen 中创建对象时,OnClose 才是正确的,这是不可能的。
  • @nolaspeaker:即使是使用ShowModal 模态显示的表单也可以在其生命周期内多次显示和关闭,因此通常OnClose 在那里也很危险。但我知道你在谈论标准的成语with TMyForm.Create do try ShowModal finally Free; end,在这种情况下OnClose likely 只被调用一次。尽管如此,“可能”还不够好,你永远不知道未来的程序员是否会以不同的方式使用这种形式。所以@Sertac 对他的评论很满意。 OnDestroy 是正确的地方,即使在 OnClose 可能工作的情况下。

标签: delphi


【解决方案1】:

“正确”的方式

通常,如果表单具有指向您在OnCreate 处理程序(例如FormCreate)中创建的对象的私有字段,并且您希望在表单的整个生命周期内都可用,您可以在表单的OnDestroy 处理程序(例如FormDestroy):

unit Unit1;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Registry;

type
  TForm1 = class(TForm)
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
  private
    FRegistry: TRegistry;
    FBitmap: TBitmap;
    FList: TStringList;
  public
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.FormCreate(Sender: TObject);
begin
  FRegistry := TRegistry.Create;
  FBitmap := TBitmap.Create;
  FList := TStringList.Create;
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  FList.Free;
  FBitmap.Free;
  FRegistry.Free;
end;

end.

这样,它们将在您的表单的整个生命周期内都存在。

在某些情况下,您可能会在以后创建对象——或者甚至让变量在不同的时间指向不同的对象——但如果它们属于表单,您通常会使用此示例中的表单来释放它们. (回想一下,类成员变量总是被初始化的,并且在 nil 变量上调用 Free 是非常安全的,因为 Free 基本上是 if Assigned then Destroy。)

为什么不OnClose

为此使用OnClose 处理程序不是很安全,因为它可能会在表单的生命周期内被多次调用。如果你在其中做X.Free,你最终会得到X是一个非nil指向垃圾的指针。

例如,以下代码是一个错误:

// BUG!! Don't do this!
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  FList.Free;
  Action := caNone;
end;

尝试关闭表单两次。

即使你不使用caNone,也有可能这是一个在其生命周期内多次打开和关闭的表单;那么您可能不想在第一次关闭它时释放它的所有对象。 (而且您当然不想在不将指针设置为nil 的情况下释放它们!)

奖金闲聊

如果您让您的字段在不同时间指向不同的对象,您必须确保以安全的方式“重新指向”该字段。例如,以下方法是不好的:

procedure RecreateFrog;
begin
  FFrog.Free;
  FFrog := TFrog.Create(ftBig, clRed, 123);
end;

原因是构造函数TFrog.Create 可能会失败(引发异常),在这种情况下,您最终会得到FFrog 是指向垃圾的非nil 指针。相反,更安全的方法是

procedure RecreateFrog;
begin
  FreeAndNil(FFrog);
  FFrog := TFrog.Create(ftBig, clRed, 123);
end;

在这种情况下,如果无法构造新的青蛙对象,FFrog 将是一个 nil 指针。当然,在这样的情况下,每次你想使用当前的青蛙时,你总是要测试 FFrog 是否被分配。

当然,还有其他的做事方式,有时你有你知道永远不会失败的构造函数等等。基本规则是你应该知道你在做什么,并且应该保证代码永远不会泄漏任何资源,访问悬空指针等。

【讨论】:

  • 为什么要使用 OnCreate 和 OnDestroy 而不是构造函数和析构函数?
  • @DavidHeffernan:没有特别的原因。这两种方法都有意义。 (不过,对于新的 Delphi 程序员,OnCreateOnDestroy 可能更容易访问,因为他们在创建这些事件处理程序时从 IDE 获得帮助。)
  • 我从来没有真正接受过这些活动,因为现在你必须学习两种不同的方法来做同样的事情。因为在某些时候我们都编写了不是从 TForm 派生的类。
  • @David:确实如此。 (当然,这些事件并没有什么“错误”,我怀疑它们在表单中的使用频率会稍微高一些。)
  • 我同意使用这些事件没有任何问题,但是你确实说它们是“正确”的方式,我想知道你是否有理由更喜欢它们。不过没关系。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-06-08
  • 2021-02-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多