Delphi技术问询:TDemo.TBuilder为何未自动释放引发内存泄漏?
为什么TDemo.TBuilder没有自动释放并导致内存泄漏?
这个问题的核心在于你没让TInterfacedObject的引用计数机制真正生效——虽然TBuilder继承自TInterfacedObject,但只有当对象被赋值给接口类型变量时,Delphi的自动引用计数(ARC)才会启动工作。
问题根源分析
看你的Using方法代码:
class procedure TDemo.Using; begin with TDemo.TBuilder.Create.Generate do try // 使用TDemo finally Free; end; end;
这里TDemo.TBuilder.Create确实创建了TBuilder实例,但你直接链式调用了Generate方法,没有把这个实例赋值给任何接口类型变量。
TInterfacedObject的引用计数逻辑是这样的:只有当对象被赋值给接口变量时,才会触发_AddRef增加引用计数;当接口变量离开作用域时,会触发_Release减少计数,计数归0时自动销毁对象。而你现在的写法里,TBuilder实例从创建到调用完Generate,全程没有触发任何引用计数操作,自然没人去释放它,最终造成内存泄漏。
解决方案
要让TBuilder被自动释放,你需要做两件关键的事:
给TBuilder定义对应的接口类型
先在TDemo的public区块里声明接口,让TBuilder实现这个接口:TDemo = class public type IBuilder = interface ['{这里替换成你自己生成的GUID}'] function Generate: TDemo; end; TBuilder = class(TInterfacedObject, IBuilder) public function Generate: TDemo; end; public class procedure Using; end;用接口类型持有TBuilder实例
修改Using方法,把创建的TBuilder赋值给接口变量,让引用计数机制接管生命周期:class procedure TDemo.Using; var Builder: IBuilder; DemoInst: TDemo; begin Builder := TDemo.TBuilder.Create; // 赋值给接口,触发_AddRef DemoInst := Builder.Generate; try // 使用DemoInst finally DemoInst.Free; end; // Builder离开作用域时,自动调用_Release,计数归0后销毁TBuilder end;
如果想保留链式调用风格,也可以这样写(本质还是借助接口触发计数):
class procedure TDemo.Using; var DemoInst: TDemo; begin DemoInst := (TDemo.TBuilder.Create as IBuilder).Generate; try // 使用DemoInst finally DemoInst.Free; end; // 临时接口引用销毁时,自动触发_Release释放TBuilder end;
关键要点总结
- TInterfacedObject只是实现了接口的引用计数逻辑,但必须通过接口类型访问对象,才能激活这个逻辑。
- 直接用类的
Create方法创建实例却不赋值给接口变量,和普通TObject子类没有区别,不会自动释放。 - 只要对象被接口变量持有,就不需要手动
Free,接口离开作用域时会自动完成释放操作。
内容的提问来源于stack exchange,提问作者THSoftPoland
相关产品推荐
相关产品推荐

