gpt4 book ai didi

delphi - 为什么 TObjectList 类型的列表在迭代后会自动释放?

转载 作者:行者123 更新时间:2023-12-03 14:45:15 28 4
gpt4 key购买 nike

我对 Spring4D 框架的 TObjectList 类的行为有疑问。在我的代码中,我创建了一个几何图形列表,例如方形、圆形、三角形,每个图形都定义为单独的类。为了在列表被销毁时自动释放几何图形,我定义了一个 TObjectList 类型的列表,如下所示:

procedure TForm1.FormCreate(Sender: TObject);
var
geometricFigures: TObjectList<TGeometricFigure>;
geometricFigure: TGeometricFigure;
begin
ReportMemoryLeaksOnShutdown := true;

geometricFigures := TObjectList<TGeometricFigure>.Create();
try
geometricFigures.Add(TCircle.Create(4,2));
geometricFigures.Add(TCircle.Create(0,4));
geometricFigures.Add(TRectangle.Create(3,10,4));
geometricFigures.Add(TSquare.Create(1,5));
geometricFigures.Add(TTriangle.Create(5,7,4));
geometricFigures.Add(TTriangle.Create(2,6,3));

for geometricFigure in geometricFigures do begin
geometricFigure.ToString();
end;
finally
//geometricFigures.Free(); -> this line is not required (?)
end;
end;

如果我运行此代码,列表geometricFigures会自动从内存中释放,即使我没有调用列表上的方法Free(注意最后的注释行)堵塞)。我期望有不同的行为,我认为列表需要显式调用 Free(),因为局部变量geometricFigures 没有使用接口(interface)类型。

我进一步注意到,如果列表中的项目没有在 for-in 循环中迭代(我暂时将其从代码中删除),则列表不会自动释放,并且会出现内存泄漏。

这让我想到以下问题:为什么 TObjectList (geometricFigures) 类型的列表在迭代其项目时​​会自动释放,但如果从代码中删除 for-in 循环则不会?

更新

我听从了 Sebastian 的建议并调试了析构函数。列表项被以下代码破坏:

{$REGION 'TList<T>.TEnumerator'}

constructor TList<T>.TEnumerator.Create(const list: TList<T>);
begin
inherited Create;
fList := list;
fList._AddRef;
fVersion := fList.fVersion;
end;

destructor TList<T>.TEnumerator.Destroy;
begin
fList._Release;
inherited Destroy; // items get destroyed here
end;

更新

我不得不重新考虑我接受的答案并得出以下结论:

在我看来,鲁迪的答案是正确的,即使所描述的行为可能不是框架中的错误。我认为鲁迪提出了一个很好的论点,他指出框架应该按预期工作。当我使用 for-in 循环时,我希望它是只读操作。之后清除列表并不是我预期会发生的情况。

另一方面,Fritzw 和 David Heffernan 指出 Spring4D 框架的设计是基于接口(interface)的,因此应该以这种方式使用。只要这种行为被记录下来(也许 Fritzw 可以给我们提供文档引用),我同意 David 的观点,即我对框架的使用是不正确的,尽管我仍然认为框架的行为具有误导性。

我在使用 Delphi 进行开发方面没有足够的经验来评估所描述的行为是否实际上是一个错误,因此撤销了我接受的答案,对此感到抱歉。

最佳答案

使用 for ... do 进行迭代,该类必须有 GetEnumerator方法。这显然将其自身(即 TObjectList<> )返回为 IEnumerator<TGeometricFigure> 界面。迭代后,IEnumerator<>被释放,其引用计数达到0,对象列表被释放。

这是您经常在 C# 中看到的模式,但在那里,它没有这种效果,因为类实例仍然被引用,并且垃圾收集器不会跳转。

但是,如您所见,在 Delphi 中这是一个问题。我想解决方案是 TObjectList<>有一个单独的(可能是嵌套的)类或记录来执行枚举,并且不返回 Self (如 IEnumerator<> )。但这取决于Spring4D的作者。您可以将此问题提请 Stefan Glienke 注意。

更新

您的附录表明实际情况并非如此。 TObjectList<> (或者更准确地说,它的祖先 TList<> )返回一个单独的枚举器,但这确实(IMO完全没有必要,即使列表从一开始就用作接口(interface)) _AddRef/_Release而后者才是罪魁祸首。

注意

我看到多个声明,在 Spring4D 中,类不应该被用作类。那么这样的类不应该暴露在 interface 中部分,但在implementation代替该单元的部分。如果公开此类,作者应该期望用户使用它们。如果它们可以用作类,那么 for-in循环不应释放容器。其中之一是设计问题:要么作为类暴露,要么自动释放。所以存在一个错误,IMO。

关于delphi - 为什么 TObjectList 类型的列表在迭代后会自动释放?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38162508/

28 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com