Delphi7,传递对象的接口 - 在释放对象时导致无效的指针操作 [英] Delphi7, passing object's interface - causes Invalid Pointer Operation when freeing the object

查看:217
本文介绍了Delphi7,传递对象的接口 - 在释放对象时导致无效的指针操作的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个实现接口的类,可用于插件。
类的声明非常简单。整个应用程序只有一个此类的实例。当调用返回接口的函数时,它会在检索到的接口上调用_AddRef,然后再将其作为结果传回。不幸的是,它一直有效,直到我尝试释放对象(参见finalization部分) - 它报告无效的指针操作。如果我注释掉它,它工作正常(但是FastMM报告内存泄漏,因此对象没有被释放)。



这是函数中代码的一部分返回接口(事实上它是我的ServicesManager类的重写的QueryInterface)。

 如果ConfigManager.GetInterface(IID) ,obj)然后
开始
ISDK_ConfigManager(obj)._ AddRef;
结果:= 0;
结束

和ConfigManager类的代码......

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

var
ConfigManager:TConfigManager;
实现

...

初始化
ConfigManager:= TConfigManager.Create();
finalization
如果ConfigManager<> nil然后
FreeAndNil(ConfigManager); //如果我发表评论,它会泄漏内存,但没有无效的Ptr。欧普。筹集

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

解决方案

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



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



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



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

I have a class that implements an interface, which is made available for plugins. The declaration of class is quite simple. There is only one instance of this class for an entire application. When the function that returns the interface is called, it calls _AddRef on the retrieved interface before passing it back as result. Unfortunately it works until I try to free the object (see "finalization" section) - it reports Invalid Pointer Operation. If I comment it out, it works fine (however FastMM reports memory leaks, so the object is not being freed).

Here is the part of the code in the function that returns the interface (in fact it is an overridden QueryInterface of my "ServicesManager" class).

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

and the code of ConfigManager class ...

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

What am I doing wrong? I need to pass a reference to exactly this instance of ConfigManager.

解决方案

The number one piece of advice you'll hear when dealing with interfaces is to never mix interface references with object references. What this means is that once you start referring to an object via an interface reference, you cease to refer to it via an object reference. Ever.

The reason is that the first time you assign an interface variable, the reference count of the object will become 1. When that variable goes out of scope or gets assigned a new value, the reference count becomes zero, and the object frees itself. This is all without any modification of the original object-reference variable, so when you later try to use that variable, it's not a null pointer, but the object it referred to is gone — it's a dangling reference. When you try to free something that doesn't exist, you get an invalid-pointer-operation exception.

Declare your ConfigManager variable as an interface. Don't free it yourself. Once you do that, you can move the entire declaration of TConfigManager into the implementation section because no code outside that unit will ever refer to it.

Also, there's rarely any reason to provide your own implementation of QueryInterface. (You said you overrode it, but that's impossible since it's not virtual.) The one provided by TInterfacedObject should be sufficient. The one you're providing is actually causing a memory leak because you're incrementing the reference count when you shouldn't be. GetInterface already calls _AddRef (by performing an interface assignment), so you're returning objects with inflated reference counts.

这篇关于Delphi7,传递对象的接口 - 在释放对象时导致无效的指针操作的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆