当前位置 : 主页 > 编程语言 > delphi >

Delphi7,传递对象的接口 – 在释放对象时导致无效的指针操作

来源:互联网 收集:自由互联 发布时间:2021-06-23
我有一个实现接口的类,它可用于插件. 课堂宣言很简单.整个应用程序只有一个此类的实例.当调用返回接口的函数时,它会在检索到的接口上调用_AddRef,然后再将其作为结果传回.不幸的是
我有一个实现接口的类,它可用于插件.
课堂宣言很简单.整个应用程序只有一个此类的实例.当调用返回接口的函数时,它会在检索到的接口上调用_AddRef,然后再将其作为结果传回.不幸的是,它一直有效,直到我尝试释放对象(参见“终结”部分) – 它报告无效的指针操作.如果我将其注释掉,它可以正常工作(但是FastMM报告内存泄漏,因此对象没有被释放).

这是函数中返回接口的部分代码(实际上它是我的“ServicesManager”类的重写的QueryInterface).

if ConfigManager.GetInterface(IID, obj) then
begin
  ISDK_ConfigManager(obj)._AddRef;
  result:= 0;
end

和ConfigManager类的代码……

type
  TConfigManager = class(TInterfacedObject, ISDK_ConfigManager)
  private
  ... 
  end;

var
  ConfigManager: TConfigManager;
implementation

...

initialization
  ConfigManager:= TConfigManager.Create();
finalization
  if ConfigManager <> nil then
    FreeAndNil(ConfigManager); //if I comment it out, it leaks the memory but no Invalid Ptr. Op. raises

我究竟做错了什么?
我需要传递一个对ConfigManager这个实例的引用.

在处理接口时,您将听到的第一条建议是永远不要将接口引用与对象引用混合在一起.这意味着一旦您通过接口引用开始引用对象,您就不再通过对象引用来引用它.永远.

原因是第一次分配接口变量时,对象的引用计数将变为1.当该变量超出范围或被赋予新值时,引用计数变为零,并且对象将自行释放.这一切都没有对原始对象引用变量进行任何修改,因此当您稍后尝试使用该变量时,它不是空指针,但它引用的对象已经消失 – 它是一个悬空引用.当您尝试释放不存在的内容时,会出现无效指针操作异常.

将ConfigManager变量声明为接口.不要自己解脱.一旦你这样做,你可以将TConfigManager的整个声明移动到实现部分,因为该单元之外的代码不会引用它.

此外,很少有任何理由提供您自己的QueryInterface实现. (你说你覆盖了它,但这是不可能的,因为它不是虚拟的.)TInterfacedObject提供的那个应该足够了.你提供的那个实际上是导致内存泄漏,因为当你不应该增加引用计数时. GetInterface已经调用_AddRef(通过执行接口赋值),因此您将返回具有夸大引用计数的对象.

网友评论