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

时间:2009-09-17 00:45:08

标签: delphi pointers interface

我有一个实现接口的类,可用于插件。 课堂宣言很简单。整个应用程序只有一个此类的实例。当调用返回接口的函数时,它会在检索到的接口上调用_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实例的引用。

4 个答案:

答案 0 :(得分:10)

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

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

将您的ConfigManager变量声明为接口。不要自己解脱。完成后,您可以将TConfigManager的整个声明移动到实现部分中,因为该单元外的代码不会引用它。

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

答案 1 :(得分:2)

你说这是一个插件系统?您是否将插件作为BPL加载?实际上,上周我遇到了这个问题。您不能依赖 finalization 来清除您的界面引用。在卸载插件之前,您需要确保清除它们,否则它的内存空间将无效。

编辑:通过“清除接口引用”,我的意思是通过手动将其设置为nil或通过让引用超出范围来调用它们的_Release。如果您的接口管理器持有对插件的接口引用,那么当接口管理器被破坏时它们将被清除。

答案 2 :(得分:1)

我完全赞同罗布。

最有可能帮助的是重写您的初始化代码,如下所示。

现在ConfigManager的类型为ISDK_ConfigManager,并且通过为其指定nil,引用计数将减少。 当引用计数变为零时,它将自动被释放。

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

var
  ConfigManager: ISDK_ConfigManager;
implementation

...

initialization
  ConfigManager:= TConfigManager.Create();
finalization
  ConfigManager := nil;
end;

- 的Jeroen

答案 3 :(得分:0)

TConfigManager类是否有任何声明为“已发布”的方法?