首页 > 解决方案 > 如何从内存中释放存储在 TreeView 中的对象?

问题描述

我使用 Delphi 7。我创建了一个存储信息的记录,并使用指针将该记录作为一个对象存储在具有 100 多个项目的 TreeView 中。

我的问题是,如何从内存中释放或消除所有这些对象?

type
  PMyRec = ^TMyRec;
  TMyRec = record
    Tipo: string;
    parent: string;
  end;    

var    
  MyRecPtr: PMyRec;

for x := 1 to 100 do
begin
  New(MyRecPtr);
  MyRecPtr^.Tipo := '1';
  MyRecPtr^.parent := 'paul';
  Tree1.Items.AddChildObject(nil, IntToStr(x) + '-NewItem', MyRecPtr);
  ListaDePonteiros.Add( MyRecPtr ); // I use a TList to store pointers
  ListaDeObjectos.Add( MyRecPtr ); // I use a TList to store Objects
end;

我如何尝试将它们全部删除:

procedure TForm1.Button2Click(Sender: TObject);
  procedure EmptyTList(Var AList: TList);
  var
    intContador: integer;
  begin
    for intContador := (AList.Count-1) downto 0 do
    begin
      if Assigned(AList.Items[intContador]) then
      begin
        Dispose(AList.Items[intContador]);
        AList.Items[intContador] := nil;
        AList.Delete(intContador);
      end;
    end;
  end;
begin
  if Assigned(MyRecPtr) then
  begin
    EmptyTList(ListaDePonteiros);
  end;
end;

当我删除 TreeViewOnDelete事件中的所有项目时,我有这个:

if assigned(Node.Data) then
begin
  Dispose(Node.Data);
end;

我想要做的是从内存中释放所有对象!

如果我使用该列表处理所有对象,那么如果我从 TreeView 中删除任何项目,则会引发无效指针错误!

即使处理了所有指针,MyRecPtr仍然指向内存中的某个位置,而且Node.Data

标签: objectpointersdelphi-7new-operatordispose

解决方案


您的代码正在崩溃,因为您两次释放相同的内存,因为您没有定义任何明确的记录实例所有权。

您的ListaDePonteirosListaDeObjectos列表是多余的,可以删除。TTreeView可以是记录的所有者,您可以在事件中简单地拥有Dispose()它们TTreeView.OnDeletion并完成它1

var
  MyRecPtr: PMyRec;

for x := 1 to 100 do
begin
  New(MyRecPtr);
  try
    MyRecPtr^.Tipo := '1';
    MyRecPtr^.parent := 'paul';
    Tree1.Items.AddChildObject(nil, IntToStr(x) + '-NewItem', MyRecPtr);
  except
    Dispose(MyRecPtr);
    raise;
  end;
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
  Tree1.Items.Clear;
end;

procedure TForm1.Tree1Deletion(Sender: TObject; Node: TTreeNode);
begin
  if Assigned(Node.Data) then
    Dispose(PMyRec(Node.Data));
end;

否则,如果您选择保留一个单独的列表,请保留该ListaDeObjectos列表并删除该ListaDePonteiros列表(因为没有理由维护 2 个跟踪完全相同的值的列表)。您只需要决定是否想要ListaDeObjectosTree1拥有您分配的记录:

  • 如果ListaDeObjectos要成为所有者,请不要Dispose(Node.Data)TTreeView.OnDeletion活动中致电。

    var
      MyRecPtr: PMyRec;
      Idx: Integer;
    
    for x := 1 to 100 do
    begin
      New(MyRecPtr);
      try
        MyRecPtr^.Tipo := '1';
        MyRecPtr^.parent := 'paul';
        Idx := ListaDeObjectos.Add(MyRecPtr);
        try
          Tree1.Items.AddChildObject(nil, IntToStr(x) + '-NewItem', MyRecPtr);
        except
          ListaDeObjectos.Delete(Idx);
          raise;
        end;
      except
        Dispose(MyRecPtr);
      end;
    end;
    
    procedure TForm1.Button2Click(Sender: TObject);
      procedure EmptyTList(AList: TList);
      var
        intContador: integer;
      begin
        for intContador := 0 to (AList.Count-1) do
          Dispose(PMyRec(AList[intContador]));
        AList.Clear;
      end;
    begin
      Tree1.Items.Clear;
      EmptyTList(ListaDePonteiros);
    end;
    
  • 如果Tree1要成为所有者,请不要打电话(Dispose(AList.Items[intContador])实际上EmptyTList(),您可以完全摆脱EmptyTList()并在需要时打电话ListaDeObjectos.Clear())。

    var
      MyRecPtr: PMyRec;
      Node: TNode;
    
    for x := 1 to 100 do
    begin
      New(MyRecPtr);
      try
        MyRecPtr^.Tipo := '1';
        MyRecPtr^.parent := 'paul';
        Node := Tree1.Items.AddChildObject(nil, IntToStr(x) + '-NewItem', MyRecPtr);
      except
        Dispose(MyRecPtr);
        raise;
      end;
      try
        ListaDePonteiros.Add(MyRecPtr);
      except
        Node.Free;
        raise;
      end;
    end;
    
    procedure TForm1.Button2Click(Sender: TObject);
    begin
      ListaDePonteiros.Clear;
      Tree1.Items.Clear;
    end;
    
    procedure TForm1.Tree1Deletion(Sender: TObject; Node: TNode);
    begin
      if Assigned(Node.Data) then
        Dispose(PMyRec(Node.Data));
    end;
    

无论哪种方式,当不进行大规模清理时Tree1,请考虑在删除单个节点时ListaDeObjectos调用事件以保持和同步:ListaDeObjectos.Remove()TTreeView.OnDeletionTree1ListaDeObjectos

procedure TForm1.Tree1Deletion(Sender: TObject; Node: TNode);
begin
  if Assigned(Node.Data) then
  begin
    // only if the TreeView is the owner...
    Dispose(PMyRec(Node.Data));

    ListaDeObjectos.Remove(Node.Data);
  end;
end;

1. 每当您执行Dispose()记录实例时,请确保您对原始指针进行类型转换PMyRec,否则编译器将无法正确完成记录的成员,从而导致内存泄漏。


推荐阅读