释放具有MainForm作为所有者的表单是一个错误吗?

时间:2016-12-06 16:52:36

标签: delphi delphi-10.1-berlin

在我们现有的代码中,我们有很多这样的代码,其中使用MainForm作为所有者创建表单(而不是nil),但我们明确地释放它。

function SomeFunc(): Boolean;
var
  form: TMyForm; // subclasses TForm
begin
  with TMyForm.Create(Application.MainForm) do
    try
      ShowModal;
      Exit(True);
    finally
      Free;
    end
end;

这会导致任何形式的错误或崩溃,还是安全?

我似乎无法通过阅读文档来解决这个问题:

http://docwiki.embarcadero.com/Libraries/Berlin/en/System.Classes.TComponent.Owner

http://docwiki.embarcadero.com/Libraries/Berlin/en/System.Classes.TComponent.Create

3 个答案:

答案 0 :(得分:8)

查看源代码,您可以自己回答这个问题!

TForm从TComponent深入继承,如果我们看一下TComponent的析构函数,我们看到这个(至少在DelphiXE7中):

destructor TComponent.Destroy;
begin
  Destroying;
  RemoveFreeNotifications;
  DestroyComponents;
  if FOwner <> nil then FOwner.RemoveComponent(Self);
  FObservers.Free;
  inherited Destroy;
end;

这里有两条重要的路线:

  • DestroyComponents

这将在销毁所有者本身之前销毁所有拥有的组件。

  • if FOwner <> nil then FOwner.RemoveComponent(Self);

通知所有者他拥有的对象不再存在,并且必须从所有者组件列表中删除它。

所以在你的情况下,Application.MainForm将拥有你的TMyForm实例,但是在销毁时它将从主表单组件列表中消失。

总而言之,您的代码完全正常,不会崩溃。但要明确控制组件的生命周期,您应该将 nil 作为所有者传递给构造函数。正如Sertac Akyuz在评论中已经提到的那样,你将避免调用FOwner.RemoveComponent(Self);来节省一些CPU周期......

答案 1 :(得分:2)

这不是一个bug,但绝对是代码味道。这样做的首选方式(模态形式的create-show-destroy循环)是:

  function SomeFunc(): Boolean;
  var
    form: TMyForm;
  begin
    form := TMyForm.Create(nil);
    try
      Result := form.ShowModal = mrOk; // this can vary, of course
    finally
      form.Release;
    end
  end;

请注意,我不是直接打电话给 TForm.Free 。相反,我正在调用更强大的 TForm.Release 方法。来自RAD Studio帮助文件:

  

在表单上的组件的表单和事件处理程序的所有事件处理程序都已完成执行之前,Release不会销毁表单。 Release还保证在表单发布之前处理表单事件队列中的所有消息。表单或其子代的任何事件处理程序都应使用Release而不是Free(Delphi)或delete(C ++)。如果不这样做可能会导致内存访问错误。

答案 2 :(得分:1)

据我所知,如果确保您的主表单在您的子表单显示时没有被释放,那将非常安全。我预计会违反&#34;访问违规行为&#34;或&#34;指针操作无效&#34;在ChildForm.Free上,也就是说,如果它成功那么远。

在一个小的测试用例中,应用程序实际上仍然停留在(现已销毁的)第二种形式的模态循环中。

但是,由于释放主表单是TApplication的最佳工作,我认为我们可以使用主表单作为您孩子表单的所有者完全没问题。

关于问题的终身管理方面,请参阅whosrdaddy的答案。