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

Delphi中通过引用计数接口创建的窗体如何正确释放?

Delphi中实现接口的窗体释放方案

问题场景

现有窗体类实现了自定义接口:

type
  IMyInterface = interface
    ['{50E7E215-A8EA-4A1C-9F1E-018E4A76DCBD}']
    procedure DoSomething;
  end;

  TFormOther = class(TForm, IMyInterface)
    // 窗体成员及接口实现
    procedure DoSomething;
  end;

在工厂类中创建窗体并通过接口调用方法:

TFactory = class(TInterfacedObject)
public
  procedure MakeIt;
end;

procedure TFactory.MakeIt;
var
  LMyIntf: IMyInterface;
begin
  LMyIntf := TFormOther.Create(nil);
  LMyIntf.DoSomething;

  // 直接转换后Free会触发报错:FastMM检测到尝试使用已释放对象的接口
  // (LMyIntf as TFormOther).Free;
end;

若不手动释放TFormOther实例会造成内存泄漏,除了在TFormOther.FormClose中设置Action := TCloseAction.caFree,还有其他可行方案:

可行方案

1. 手动实现接口引用计数逻辑

由于TForm继承自TComponent而非TInterfacedObject,默认不会通过接口引用计数自动释放对象。可以在窗体类中手动实现IInterface的三个方法,让引用计数归零时自动销毁窗体:

type
  TFormOther = class(TForm, IMyInterface)
  private
    FRefCount: Integer;
    function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall;
    function _AddRef: Integer; stdcall;
    function _Release: Integer; stdcall;
  public
    procedure DoSomething;
  end;

function TFormOther.QueryInterface(const IID: TGUID; out Obj): HResult;
begin
  if GetInterface(IID, Obj) then
    Result := S_OK
  else
    Result := E_NOINTERFACE;
end;

function TFormOther._AddRef: Integer;
begin
  Result := InterlockedIncrement(FRefCount);
end;

function TFormOther._Release: Integer;
begin
  Result := InterlockedDecrement(FRefCount);
  if Result = 0 then
    Destroy;
end;

此方案下,当接口变量LMyIntf离开作用域时,引用计数归0,窗体自动释放,无需手动调用Free。

2. 手动断开接口引用后释放窗体

如果不想修改窗体类,可以在工厂方法中先断开接口引用,再释放窗体实例:

procedure TFactory.MakeIt;
var
  LMyIntf: IMyInterface;
  LForm: TFormOther;
begin
  LForm := TFormOther.Create(nil);
  LMyIntf := LForm;

  LMyIntf.DoSomething;

  LMyIntf := nil; // 先清空接口引用,消除计数关联
  LForm.Free;    // 再安全释放窗体
end;

此方式避免了接口引用与窗体实例的生命周期冲突,不会触发FastMM报错。

3. 窗体关闭时自动释放(原方案)

在TFormOther的FormClose事件中设置Action := caFree:

procedure TFormOther.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  Action := TCloseAction.caFree;
end;

此方案适合需要用户手动关闭窗体的场景,用户关闭窗体时自动释放资源,同时需注意避免在窗体释放后仍持有接口引用。

方案对比

  • 接口引用计数实现:适合无用户交互、完全由接口管理生命周期的场景,自动化程度高。
  • 手动断开引用释放:适合一次性使用的窗体,逻辑直观,无需修改窗体类。
  • 关闭事件自动释放:适合需要用户操作关闭的场景,符合窗体交互习惯。

内容的提问来源于stack exchange,提问作者Jaco Crafford

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.16 07:01:09