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
相关产品推荐
相关产品推荐

