代码之家  ›  专栏  ›  技术社区  ›  jpfollenius Rob Kennedy

对象的引用计数

  •  7
  • jpfollenius Rob Kennedy  · 技术社区  · 15 年前

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

    type TFileInfo = class (TInterfacedObject, IInterface)
    

    删除了我对tfileinfo.free的所有手动调用。不幸的是,Delphi报告了大量内存泄漏。继续搜索,我发现下面的问题解释了为什么这不起作用:

    Why aren't descendants of TInterfacedObject garbage collected?

    这里有一个解决方法,但它要求我(至少如果我做得正确)编写一个自定义接口ifileinfo,并为它提供许多getter和setter,这是我想要避免的。

    编辑 我应该补充一下,我将create fileinfo对象插入到两种不同类型的哈希表中:一种是从tbucketlist下降的,另一种是从codegear论坛实现的哈希图。在内部,它们都是用户指针,所以情况与另一个问题一样。

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

    7 回复  |  直到 10 年前
        1
  •  5
  •   Kcats    15 年前

    不幸的是,Delphi编译器只在使用接口(在您的例子中是自定义接口ifileinfo)时生成必需的代码到inc/dec引用计数。此外,如果接口被强制转换为指针(或针对这一点的tobject),那么就不可能进行引用计数。例如,assumming global variable list:tlist:

    var ifi : IFileInfo;
    begin
      ifi := TFileInfo.Create;
      list.Add(TFileInfo(ifi));
    end;
    

    方法返回后,list[list.count-1]将包含悬空指针。

    因此,不能在将接口强制转换为指针的哈希映射中使用接口,哈希映射实现必须将它们保持为接口。

        2
  •  8
  •   Jim McKeeth    15 年前

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

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

    1. 巴里凯利写了一篇关于 Smart Pointers .它使用Delphi2009中的泛型,但我敢肯定,如果您还没有使用2009,您可以将其硬编码为您正在使用的类型的特定版本(这是一个很棒的btw版本)。

    2. 另一种处理更多版本Delphi和更少修改的方法是 value type wrapper 作者:Janez Atmapuri Makovsek。它是为tstringlist实现的一个示例,但是您可以将它用于任何类型。

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

    这是关于德尔福的事情,有一种实现事情的自由方式。在我看来,选项1是最好的选项-获取Delphi2009,如果可以的话使用该方法。

    祝你好运!

        3
  •  3
  •   Toon Krijthe    15 年前

    此功能提供给接口,但不提供给对象。

    您可以创建类似的内容,但需要重写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;
    

    您需要refcountedcopy将对象分配给另一个变量。但是你有一个refcounted对象。

    如何使用:

    var1 := TRefCountObject.Create;   // rc = 1
    var2 := var1.RefCountedCopy;      // rc = 2
    var3 := var1.RefCountedCopy;      // rc = 3
    var2.Free;                        // rc = 2
    var1.Free;                        // rc = 1
    var4 := var3.RefCountedCopy;      // rc = 2
    var3.Free;                        // rc = 1
    var4.Free;                        // rc = 0
    
        4
  •  3
  •   Jim McKeeth    15 年前

    如果您希望消除对tobject实例的空闲调用,那么您可能需要查看本机delphi的垃圾收集器。我知道两种不同的垃圾收集器和一种垃圾收集技术,每个都有利弊。

    其中一个可能对你有用。

        5
  •  3
  •   Steffen Binas    10 年前

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

    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;
    
        6
  •  1
  •   Francesca    15 年前

    要添加已经说过的内容,如果要存储对接口的引用,而不是使用tlist,请使用 制作者 . 参考计数将持续工作。

        7
  •  0
  •   Stijn Sanders    15 年前

    对此有一个很长的解释,但简而言之:继承TinterfacedObject(而不是调用free yourself)是不够的,您需要使用对象工厂动态来为您创建对象,并在任何地方使用对象的接口指针,而不仅仅是对象引用变量。(是的,这意味着你不能不看就把“旧代码”换掉)

    推荐文章