我有一个实现接口的类,它可用于插件. 课堂宣言很简单.整个应用程序只有一个此类的实例.当调用返回接口的函数时,它会在检索到的接口上调用_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(通过执行接口赋值),因此您将返回具有夸大引用计数的对象.