释放添加到TStringList中的对象

3

我的做法正确吗?
我正在使用记录填充TStringList中的对象,我在填充过程中创建这些记录。 我已经将TStringList.OwnsObjects设置为true。但是,当我关闭应用程序时,会发生访问冲突。以下是我的代码摘录。

type
  PUsageData = ^TUsageData;
  TUsageData = record
    DeclaredIn: String;
    LineNumber: Integer;
    TotalUsage: Integer;
  end;

...

var VarUsages: TStringList;    // object contains a TUsageData record


procedure BuildUsageList;
var idx, idy, n,m: Integer;
    sl: TStringList;
    s,t: String;
    rec: PUsageData;
begin
  VarUsages.Clear;
  sl:= TStringList.Create;
  s := '';
  // First make a list and create a record for each variable declared in program
  for idx := 0 to IncludeList.Count -1 do begin
    GetSource(IncludeList[idx], sl);
    for idy := 0 to sl.Count -1 do begin
      t := '';
      t := CodeAnalyser.GetItems(sl[idy], caVariables);
      if t > '' then begin
        system.New(Rec);
        rec.DeclaredIn := IncludeList[idx];
        rec.LineNumber := idy;
        rec.TotalUsage := 0;
        VarUsages.AddObject(t, TObject(rec));
      end;
    end;
  end;
end;

...

initialization
  VarUsages := TStringList.Create;
  VarUsages.OwnsObjects := true;

finalization
  VarUsages.Free;
end.

我正在使用Delphi 10.1。

我也想知道当我清空StringList时会发生什么。它会处理Objects吗?


4
你正在使用记录(records)而不是对象(objects),这里呈现的代码不会释放这些记录(你不能让一条记录充当一个“TObject”)。为什么不使用一个简单的类呢? - whosrdaddy
指针不是对象。无论如何,您正在使用错误的类。将字符串放入记录中并使用TList<TUsageData>。尽管现在字符串列表大多数都是多余的。 - David Heffernan
1个回答

4
你至少有三种方法来处理这个问题:

1. 手动处理记录

因为你正在存储记录指针而不是TObject的子类,所以你不能依赖于OwnsObjects。将其设置为False

在你释放TStringList之前,你应该手动Dispose每个记录:

for I := 0 to VarUsages.Count - 1 do
  Dispose(PUsageData(VarUsages.Objects[i]));

请注意,将类型转换为PUsageData对于Dispose的正常工作非常重要。

由于您将OwnsObjects设置为False,因此字符串列表不会尝试释放“对象”,因此您可以立即调用:

VarUsages.Free;

请注意,Dispose 将负责正确地完成记录的最终操作,包括其中的字符串。 注意:正如David Heffernan所评论的那样:除了在销毁字符串列表时手动处理外,每当您删除或修改项时都必须这样做。在我看来,这使得另外两个选项更加有吸引力。
2.使用对象(类实例)而不是记录
另一种选择是将TUsageData 转换为简单的类(没有方法,没有属性,只有公共字段):
type
  TUsageData = class
  public
    DeclaredIn: string;
    LineNumber: Integer;
    TotalUsage: Integer;
  end;

然后你可以将OwnsObjects设置为True,并让字符串列表来负责释放它们。这种情况也适用于ARC管理的目标。您使用Rec:=TUsageData.Create;而不是New。据我所知,您可以使用相同的代码。
不使用字符串列表,而是使用类似TList<TUsageData>的东西,其中TUsageData仍然可以保持记录状态。不需要使用指针,只需在记录中放入t字符串即可。由于您正在处理记录,因此除了TList<>本身之外,没有必要释放任何内容。释放它将同时处理其中的记录的终结。
  Rec: TUsageData; // record!
  ...
  t := CodeAnalyser.GetItems(sl[idy], caVariables);
  if t > '' then 
  begin
    Rec.t := t;
    Rec.DeclaredIn := IncludeList[idx];
    Rec.LineNumber := idy;
    Rec.TotalUsage := 0;
    VarUsages.Add(Rec);

1
除了在集合被销毁时手动处理外,您还必须在删除或修改项目时进行处理。 - David Heffernan
@David:确实。我会添加的。 - Rudy Velthuis

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接