Delphi 对象的引用计数

Delphi 对象的引用计数,delphi,interface,reference-counting,tinterfacedobject,Delphi,Interface,Reference Counting,Tinterfacedobject,在我的代码中,我使用了一个小的数据存储类,它是在不同的地方创建的。为了避免内存泄漏和简化事情,我想使用引用计数,所以我这样做了 type TFileInfo = class (TInterfacedObject, IInterface) 并删除了我对TFileInfo.Free的所有手动调用。不幸的是,Delphi报告了大量内存泄漏。继续搜索,我发现了以下问题,解释了为什么这不起作用: 这里介绍了一种解决方法,但它要求我(至少如果我做对了的话)编写一个自定义接口IFileInfo,并为它提供

在我的代码中,我使用了一个小的数据存储类,它是在不同的地方创建的。为了避免内存泄漏和简化事情,我想使用引用计数,所以我这样做了

type TFileInfo = class (TInterfacedObject, IInterface)
并删除了我对TFileInfo.Free的所有手动调用。不幸的是,Delphi报告了大量内存泄漏。继续搜索,我发现了以下问题,解释了为什么这不起作用:

这里介绍了一种解决方法,但它要求我(至少如果我做对了的话)编写一个自定义接口IFileInfo,并为它提供许多getter和setter,这是我想要避免的

编辑我应该补充一点,我将create FileInfo对象插入到两种不同类型的哈希表中:一种是从TBucketList下降的,另一种是来自Codegear论坛的哈希映射实现。在内部,它们都是用户指针,因此情况与另一个问题类似


是否有其他可能使Delphi中的对象使用引用计数?

此功能用于接口,但不用于对象

您可以创建类似的内容,但需要覆盖TObject的某些结构:

TRefCountObject = class (TObject)
private
  FRefCount : Integer;
public
  constructor Create;

  procedure Free; reintroduce;

  function RefCountedCopy: TRefCountObject;
end;


constructor TRefCountObject.Create;
begin
  inherited;
  FRefCount := 1;
end;

procedure TRefCountObject.Free;
begin
  if self=nil then Exit;
  Dec(FRefCount);
  if FRefCount<=0 then
    Destroy;
end;

function TRefCountObject.RefCountedCopy: TRefCountObject;
begin
  Inc(FRefCount);
  Result := self;
end;

不幸的是,Delphi编译器仅在您使用接口(在您的示例中是自定义接口IFileInfo)时生成inc/dec引用计数所需的代码。此外,若接口被转换为指针(或TObject),那个么引用计数也是不可能的。例如,假设全局变量列表:TList:

var ifi : IFileInfo;
begin
  ifi := TFileInfo.Create;
  list.Add(TFileInfo(ifi));
end;
方法返回后,list[list.Count-1]将包含悬空指针


因此,接口不能用于将其强制转换为指针的hashmap中,hashmap实现必须将其保留为接口。

对此有很长的解释,但简而言之:从TInterfacedObject继承(而不是自己调用Free)是不够的,您需要使用对象工厂动态为您创建对象,并在任何地方使用指向对象的接口指针,而不仅仅是对象引用变量。(是的,这意味着您不能在不查看的情况下切换“旧代码”)

不要混合对象引用和接口引用

var
  Intf: IInterface;
  Obj: TFileInfo;

begin
  // Interface Reference
  Intf := TFileInfo.Create; // Intf is freed by reference counting, 
                            // because it's an interface reference
  // Object Reference
  Obj := TFileInfo.Create;
  Obj.Free; // Free is necessary

  // Dangerous: Mixing
  Obj := TFileInfo.Create;
  Intf := Obj; // Intf takes over ownership and destroys Obj when nil!
  Intf := nil; // reference is destroyed here, and Obj now points to garbage
  Obj.Free; // this will crash (AV) as Obj is not nil, but the underlying object
            // is already destroyed
end;

Delphi中的引用计数仅在通过接口对实例进行引用时有效。一旦你混合了接口引用和类引用,你就有麻烦了

本质上,您希望引用计数,而不需要创建包含其中定义的所有方法和属性的接口。有三种方法可以做到这一点,它们大致按照我推荐的顺序排列

  • 巴里·凯利写了一篇关于他的文章。它使用了Delphi2009中的泛型,但我很确定,如果您还没有使用2009,您可以将其硬编码为您正在使用的类型的特定版本(顺便说一句,这是一个很好的版本)

  • 另一种使用更多版本的Delphi和更少修改的方法是Janez Atmapuri Makovsek的。这是一个为TStringList实现的示例,但您可以将其改编为任何类型

  • 第三种方法是创建接口指针(类似于Barry的智能指针,但不太智能)。我相信JCL中有一个,但我不记得细节。基本上,这是一个在构造时接受TObject引用的接口。然后,当它的引用计数达到零时,它将调用传递给它的对象上的free。此方法实际上只适用于不作为参数传递的短期实例,因为您将引用计数的引用与实际使用的引用分开。我会推荐另外两种方法中的一种,但是如果你喜欢这种方法并且想要更多的信息,请告诉我

  • 这就是德尔福的特点,有一种自由的方式来完成事情。选项#1在我看来是最好的选择-获取Delphi 2009,如果可以,使用该方法


    祝你好运

    要添加到已经说过的内容中,如果要存储对接口的引用,而不是使用TList,请使用TInterfaceList。ref计数将持续工作。

    如果您想消除对TObject实例的释放调用,那么您可能需要查看本机Delphi的垃圾收集器。我知道有两种不同的垃圾收集器和一种垃圾收集技术,每种都有优点和缺点

    • 亨里克·赫尔斯特罗姆

    • 巴里·凯利

    • Rossen Assenov(不是一个真正的垃圾收集器,更像是一个记忆袋。)


    其中一个可能适合您。

    谢谢您的详细回答!但我并不完全理解。我还得打电话给TRefCountObject,有空吗?或者我如何使用它?所以我仍然必须确保每个对象至少调用一次Free,对吗?没有办法避免这个问题吗?这个问题没有灵丹妙药。正如我正确理解的,你的问题是你有一个对象被两个列表引用。通常,您声明其中一个为“所有者”,负责销毁和删除其他链接。但即使只有一个列表,也需要释放每个对象。或者使用带有垃圾收集的语言。好的,那么,我将返回到手动释放对象内存的版本。我只是好奇,是否有一种方法可以借助接口“模拟”普通对象的垃圾收集。正如你和其他海报所指出的,没有。谢谢你提供的宝贵信息!但是,如果我将所有对象插入到哈希映射中,编译器是否会生成正确的inc/dec?此代码不会编译。不可能将接口类型转换为对象。它可以编译,但不起作用,这就是我试图演示的要点。我再次编辑了答案,希望现在答案更清楚。很难选择接受的答案,但我发现你的答案最清楚地描述了实际问题(基于指针的数据结构)。我再次阅读了你的问题,我
    var
      Intf: IInterface;
      Obj: TFileInfo;
    
    begin
      // Interface Reference
      Intf := TFileInfo.Create; // Intf is freed by reference counting, 
                                // because it's an interface reference
      // Object Reference
      Obj := TFileInfo.Create;
      Obj.Free; // Free is necessary
    
      // Dangerous: Mixing
      Obj := TFileInfo.Create;
      Intf := Obj; // Intf takes over ownership and destroys Obj when nil!
      Intf := nil; // reference is destroyed here, and Obj now points to garbage
      Obj.Free; // this will crash (AV) as Obj is not nil, but the underlying object
                // is already destroyed
    end;