You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何正确释放FireMonkey控件?带父窗体的子窗体及TFmxObject.Release问题

跨平台下FireMonkey带父窗体子窗体的正确释放姿势

嘿,这个问题我刚好在跨平台开发FireMonkey的时候踩过坑!确实,TFmxObject.Release不仅已经被官方标记为弃用,还只在Windows上能正常工作,Android完全不买账——这是因为FireMonkey的跨平台架构和VCL那套不一样,没有VCL依赖消息循环延迟释放的机制。

如果你想在控件自身的事件处理程序里释放它(比如子窗体的关闭按钮点击事件里释放自己),可以按照下面这些跨平台通用的方法来:

1. 用TThread.Queue延迟释放(最推荐)

这是跨平台最安全的做法,原理是把释放操作放到主消息队列的末尾,等当前的事件处理代码全部执行完之后,再去执行释放操作,从根本上避免了“事件还在执行,控件已经被释放”的访问违规问题。

举个实际的代码例子,比如子窗体的关闭按钮点击事件:

procedure TChildForm.btnCloseClick(Sender: TObject);
begin
  // 先处理你需要的业务逻辑,比如打个日志标记
  Log.d('准备释放子窗体');
  
  // 把释放操作丢到主队列,等当前事件跑完再执行
  TThread.Queue(nil, procedure
  begin
    // 如果需要,可以先断开子窗体和父窗体的关联(其实DisposeOf会自动处理,可选操作)
    // Self.Parent := nil;
    Self.DisposeOf;
    // 要是用的是比较老的Delphi版本,没有DisposeOf的话,就用Free——不过注意ARC平台下Free是引用计数减一,DisposeOf是强制立即释放
  end);
  
  // 这里的代码会正常执行完,不会被阻塞
  Log.d('当前事件处理代码执行完毕');
end;

2. ARC平台(Android/iOS)的特殊注意点

在Android、iOS这类ARC(自动引用计数)平台上,Free本质是给控件的引用计数减一,只有当引用计数降到0时才会真正释放;而DisposeOf是强制绕过ARC,直接调用控件的析构函数释放资源。所以如果你需要确保控件被立即释放(比如要在析构函数里看到日志输出),优先用DisposeOf。

3. 析构函数日志的小细节

如果你在子窗体的析构函数里加了日志,比如:

destructor TChildForm.Destroy;
begin
  Log.d('子窗体析构函数已执行');
  inherited;
end;

不管是Windows还是Android,只要用上面的TThread.Queue+DisposeOf的组合,析构函数都会被正常触发,你能看到对应的日志输出。

4. 要避开的坑

  • 绝对不要直接在事件里调用Self.Free或者Self.DisposeOf:事件触发时控件正处于被系统引用的状态,直接释放会导致后续代码访问已释放的内存,轻则崩溃,重则出现各种诡异的行为。
  • 彻底抛弃Release方法:已经被弃用,跨平台完全不兼容,别再用了。
  • 如果子窗体是用ShowModal显示的,别在模态窗体内部释放自己:应该在调用ShowModal的地方,用try-finally块来释放,比如:
procedure TMainForm.btnOpenChildClick(Sender: TObject);
var
  ChildForm: TChildForm;
begin
  ChildForm := TChildForm.Create(Self);
  try
    if ChildForm.ShowModal = mrOk then
    begin
      // 处理子窗体返回的结果
    end;
  finally
    ChildForm.DisposeOf;
  end;
end;

这样不管是Windows还是Android平台,都能正确释放控件,不会出现各种跨平台兼容问题。


内容的提问来源于stack exchange,提问作者maf-soft

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.20 08:13:45