如何在Delphi中有效地使用内存管理接口

如何在Delphi中有效地使用内存管理接口,delphi,delphi-xe,Delphi,Delphi Xe,我是Delphi的新手,一直在手动进行所有内存管理,但听说Delphi的引用能够使用接口进行引用计数,并以这种方式提供一些内存管理。我想从这开始,但有几个问题 一般来说,我该如何使用它。创建接口和实现它的类。那么,每当我需要这个对象时,变量实际上是接口类型的,但是实例化对象和presto?没有必要考虑释放它吗?没有更多的尝试了吗 为真正不需要的类创建一堆接口似乎非常麻烦。有关于自动生成这些的提示吗?我如何最好地组织它?接口和类在同一个文件中 有哪些常见的陷阱会让我感到悲伤?将接口对象强制转换为其

我是Delphi的新手,一直在手动进行所有内存管理,但听说Delphi的引用能够使用接口进行引用计数,并以这种方式提供一些内存管理。我想从这开始,但有几个问题

  • 一般来说,我该如何使用它。创建接口和实现它的类。那么,每当我需要这个对象时,变量实际上是接口类型的,但是实例化对象和presto?没有必要考虑释放它吗?没有更多的尝试了吗

  • 为真正不需要的类创建一堆接口似乎非常麻烦。有关于自动生成这些的提示吗?我如何最好地组织它?接口和类在同一个文件中

  • 有哪些常见的陷阱会让我感到悲伤?将接口对象强制转换为其类的对象会破坏我的引用计数吗?或者Delphi是否有任何不明显的方法来创建引用循环?(意思是除A使用B使用C使用A之外)


  • 如果有教程介绍这些内容,那就太好了,但我在搜索中没有找到任何内容。谢谢。

    我目前正在处理一个非常大的项目,该项目利用接口引用计数的“副作用”进行内存管理

    我个人的结论是,你最终会得到很多过于复杂的代码,没有比“我不必担心免费呼叫”更好的理由了

    我强烈反对这种做法,理由很基本:

    1) 您正在使用为COM兼容性而存在的副作用

    2) 您正在使您的对象占用空间和效率更大。接口是指向指针列表的指针。。或者类似的东西

    3) 就像你说的。。。您现在必须制作成堆的接口,唯一的目的是避免自己释放内存。。。在我看来,这会造成更多的麻烦

    4) 当对象在引用之前被释放时,最常见的bug将成为调试的一大难题。我们在自己的引用计数中有专门的代码,在软件推出之前尝试并测试这个问题

    现在回答您的问题。

    1) 给定TFoo和接口IFoo,您可以使用如下方法

    function GetFoo: IFoo;
    begin
      Result := (TFoo.Create as IFoo);
    end;
    
    …普雷斯托,你不需要最后一个来释放它

    2) 是的,就像我说的,你认为这是一个好主意,但它变成了一个巨大的痛苦

    3) 2个问题

    A) 您有Object1.Interface2和Object2.Interface1。。。由于循环引用,这些对象将永远不会被释放


    B) 在释放所有引用之前释放对象,我不能强调追踪这些bug有多困难…

    这是导致“自动垃圾收集”需求的最常见抱怨在Delphi中,即使是短暂的临时对象也必须手动处理,并且您必须编写大量的“锅炉板”代码,以确保在发生异常时发生这种情况

    例如,创建一个TStringList,用于过程中的某些临时排序或其他算法目的:

    procedure SomeStringsOperation(const aStrings: TStrings);
    var
      list: TStringList;
    begin
      list := TStringList.Create;
      try
          :
         // do some work with "list"
          :
      finally
        list.Free;
      end;
    end;
    
    正如您所提到的,实现引用计数生存期管理的COM协议的对象通过在所有对它们的引用被释放时清理自己来避免这种情况

    但是由于TStringList不是COM对象,因此您无法享受它提供的便利

    幸运的是,有一种方法可以使用COM引用计数来处理这些事情,而不必创建您希望使用的类的所有新的COM版本。您甚至不需要切换到完全基于COM的模型

    我创建了一个非常简单的实用程序类,允许我将任何对象“包装”到一个轻量级COM容器中,专门用于获得这种自动清理行为。使用此技术,您可以将上述示例替换为:

    procedure SomeStringsOperation(const aStrings: TStrings);
    var
      list: TStringList;
    begin
      AutoFree(@list);
    
      list := TStringList.Create;
    
        :
      // do some work with "list"
        :
    end;
    
    AutoFree()函数调用在编译器为过程生成的退出代码中创建一个“匿名”接口对象,该对象是Release()'d。此自动释放对象被传递一个指向引用您希望释放的对象的变量的指针。除此之外,这允许我们使用AutoFree()函数作为伪“声明”,在创建任何对象之前,将任何和所有AutoFree()调用放在方法的顶部,尽可能靠近它们引用的变量声明


    实现的全部细节,包括源代码和其他示例,都在我的博客中。

    接口的内存管理是通过的实现完成的,并且由实现

    一般来说,使用接口减少内存管理的麻烦是一个不错的主意,但您需要注意以下几点:

    • 确保实现接口的类是从
      TInterfacedObject
      派生的,或者滚动您自己的祖先类,该类为
      \u AddRef
      \u Release
    • 使用/或:因此,用户界面引用或对象实例引用都不能混用。在组件中实现接口时可能会出现问题(这些接口源自
      TComponent
      ,而不是
      TInterfacedObject
    • 不要走这条路,因为它混合了基于内存的管理和基于内存的管理
    • 观察循环接口引用(您可以到处实现提到并实现的“弱接口引用”)
    • 您需要维护额外的代码,因为您需要为您想要公开的类的部分定义接口,并保留这两个部分