Delphi中使用对象与接口时的内存泄漏及访问冲突问题求解
Delphi中使用对象与接口时的内存泄漏及访问冲突问题求解
嘿,这个问题我之前也碰到过,核心是TInterfacedPersistent的引用计数机制和手动内存管理的冲突,咱们一步步拆解:
先分析两个问题的根源
1. 第一个代码的内存泄漏
你的第一个actDuplicateExecute里,把FetchBusinessObject创建的对象直接赋值给了接口变量LAbstractBaseDTO。这时候Delphi会自动调用对象的_AddRef方法,把引用计数从0加到1。当你在finally里把LAbstractBaseDTO := nil时,又会调用_Release,引用计数减到0——而TInterfacedPersistent的_Release方法在引用计数为0时会自动调用Destroy销毁对象。
那为什么FastMM报泄漏?大概率是这两个情况之一:
CreateDuplicate返回的对象被FDataObject持有,但你的Form在程序退出时没有被正确销毁(比如手动创建的Form没调用Free),导致FDataObject没被释放;- 你的
TCountryMasterDataDTO.CreateEmpty写法有问题:里面的Create应该改成inherited Create;,否则可能导致对象初始化异常,间接引发泄漏。
2. 第二个代码的访问冲突
你把创建的对象赋值给了普通对象变量LAbstractBaseDTO,然后转成IDuplicatable接口——这时候同样会触发_AddRef,引用计数变成1。然后你在finally里手动LAbstractBaseDTO.Free销毁了对象,但接口变量(那个临时的IDuplicatable)还持有对该对象的引用。当接口变量离开作用域时,Delphi会自动调用_Release,这时候访问的是已经被销毁的内存,直接触发AV。
解决方案:避免混合两种内存管理方式
既然你明确不想用接口管理内存,只想用它做抽象,那就要严格区分手动内存管理和接口引用:
修正后的actDuplicateExecute代码
procedure TBaseDataForm.actDuplicateExecute(Sender: TObject); var LBaseObj: TAbstractBaseDTO; LDuplicatable: IDuplicatable; LNewObj: TAbstractBaseDTO; begin LBaseObj := Self.FetchBusinessObject; try // 临时转换为接口,调用方法后立即释放接口引用 LDuplicatable := LBaseObj as IDuplicatable; try LNewObj := LDuplicatable.CreateDuplicate; Self.LoadData(LNewObj); finally // 清空接口变量,让引用计数回到初始值(0),不会触发自动销毁 LDuplicatable := nil; end; finally // 手动销毁原始对象,此时引用计数是0,安全无冲突 LBaseObj.Free; end; end;
额外的优化建议
- 修复
TCountryMasterDataDTO.CreateEmpty的写法:
constructor TCountryMasterDataDTO.CreateEmpty; begin inherited Create; // 明确调用父类构造,避免歧义 end;
- 确保
FetchBusinessObject创建的对象确实是你需要的临时对象——如果只是用来调用CreateDuplicate,那这个临时对象的生命周期要严格控制,避免不必要的内存占用。 - 检查Form的销毁逻辑:如果是手动创建的Form,一定要在不用时调用
Free,确保FDataObject被正确释放。
这样调整后,既保留了接口的抽象能力,又完全用手动方式管理内存,不会再出现AV和泄漏问题了。
备注:内容来源于stack exchange,提问作者Raphi1
相关产品推荐
相关产品推荐

