【问题标题】:Delphi - How to free an object that belongs to 2 (or more) ListsDelphi - 如何释放属于 2 个(或更多)列表的对象
【发布时间】:2010-01-28 21:46:54
【问题描述】:

在我正在进行的项目中,存在多个 TList 对象包含相同项对象的情况。

基本上,有一个包含所有项目对象的主列表,然后是仅包含所有项目子集的较小列表。物品是一样的,但不是副本。

问题出现在销毁过程中。主列表被释放,这释放了所有项目。主列表通过覆盖“通知”来处理释放项目,而子列表覆盖“通知”事件,以便不会再次释放项目 - 无论如何都会失败。

但是,当使用 FastMM4 时,内存泄漏日志将项目列为泄漏内存。

那么如何释放属于 2 个或更多列表的对象呢?

顺便说一句,这不是我的代码,我只是在做一些简单的维护。如果可以的话,我想避免创建每个对象的克隆以放入单独的列表中,但是嘿,男人必须做男人必须做的事情:o)

谢谢,

布尔吉

编辑

没关系,我一定是疯了。现在 FastMM4 不会将项目标记为泄漏......只有子列表,它们实际上是主列表的子类。 这里一定有我遗漏的东西。我将进行更多测试以更清楚地了解正在发生的事情。

感谢到目前为止的所有回复。

【问题讨论】:

    标签: delphi list memory-management


    【解决方案1】:

    为什么子列表会覆盖通知事件? TList 不会释放它包含的项目。还是您在谈论 TObjectLists? 如果是 TList,您确定这些项目实际上已被释放吗?有没有代码可以做到这一点?如果没有,它们就没有被释放,fastmm 是对的:它们泄漏了。

    【讨论】:

    • 同意。如果是 tObjectLists,则使主列表拥有对象,使子列表不拥有对象。没有游戏,一切都应该清理干净。如果它们是 tList,则需要在杀死主列表之前释放它们。
    • 糟糕,打字太快了。我忘了提到 TList 也被修改,以便释放项目。我更新了主要描述。
    【解决方案2】:

    我写任何 Delphi 已经 6 年多了,但总的来说,我认为你想做这样的事情:

    • 正如您所说,主列表在其自身被释放时会释放所有项目;
    • 子列表不会尝试释放项目,因为它们是通过主列表的破坏而释放的。相反,他们只是从自己的列表中删除该项目,知道销毁正在其他地方处理。

    这相当简洁,但我认为它包含了您想要的一般模式。

    【讨论】:

    • 实际上就是这样。我不知道为什么,但我在 FastMM 中得到了我不再得到的标志。干杯!
    【解决方案3】:

    @dummzeuch 是正确的。 TList 不会释放包含的项目。

    如果列表确实是TObjectLists,那么TObjectList有所有权的概念,默认是添加到列表中的对象都是列表所有,列表销毁时释放。

    TObjectList 有一个为 true 的 OwnsObjects 参数,但是有一个重载的构造函数,它接受一个可以设置为 False 的参数,因此包含的对象不属于列表。

    在您的场景中,主列表将具有 OwnsObjects 参数的默认值,而辅助列表将被构造为不拥有所包含的对象。

    【讨论】:

    • 我实际上从未看过 TObjectList。这听起来很有趣,所以我会去了解更多。但无论如何,我似乎无法复制最初的问题,所以我暂时保留它。不过谢谢!
    【解决方案4】:

    您可以尝试在 Delphi 中对接口使用自动引用计数机制。这样,对象会在最后一个引用被清除后立即释放自己,而您不必显式释放它们。 为此,列表中包含的对象需要从 TInterfacedObject 继承并实现一些接口。

    【讨论】:

      【解决方案5】:

      这是非常低效的,但效率可能不是您在这里的首要目标,所以我应该做的是对 TList 进行子类化,为此创建一个特定的集合来跟踪 TSharedList 的所有实例,并且当一个项目是在一个列表中删除,搜索所有其他列表并删除那里的项目。

      类似这样的东西(不是线程安全的):

      unit SharedLists;
      
      interface
      uses Classes;
      
      type
        TSharedList = class(TList)
        protected
          procedure Notify(Ptr: Pointer; Action: TListNotification); override;
        public
          constructor Create;
          destructor Destroy; override;
        end;
      
      implementation
      
      var
        SharedListTracker: TList;
        RecursiveCallFlag: Boolean;
      
      procedure TSharedList.Notify(Ptr: Pointer; Action: TListNotification);
      var
        I: Integer;
      begin
        if RecursiveCallFlag then
          Exit;
        RecursiveCallFlag := True;
        try
          if Action = lnDeleted then
            for I := 0 to SharedListTracker.Count - 1 do
              if (TSharedList(SharedListTracker[I]) <> Self) and (TSharedList(SharedListTracker[I]).IndexOf(Ptr) <> -1) then
                TSharedList(SharedListTracker[I].Remove(Ptr);
        finally
          RecursiveCallControl := False;
        end;
      end;
      
      constructor TSharedList.Create;
      begin
        inherited Create;
        SharedListTracker.Add(Self);
      end;
      
      destructor TSharedList.Destroy;
      begin
        SharedListTracker.Remove(Self);
        inherited;
      end;
      
      initialization
        SharedListTracker := TList.Create;
      finalization
        SharedListTracker.Free;
      end.
      

      我很确定它不会编译(从未尝试过),但它显示了我的想法。

      【讨论】:

      • 谢谢,我明白你的意思。不过它现在似乎可以工作了,所以我认为我不需要尝试它。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-06-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多