Как освободить объекты, хранящиеся в TreeView из памяти? - PullRequest
0 голосов
/ 02 января 2019

Я использую Delphi 7. Я создаю запись, в которой хранится информация, и с помощью указателей я сохраняю эту запись как объект в TreeView с более чем 100 элементами.

Моя проблема в том, как освободить или удалить все эти объекты из памяти?

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;

Когда я удаляювсе элементы в событии TreeView OnDelete, у меня есть это:

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

Что я хочу сделать, это освободить все объекты из памяти!

Если я удаляю все объекты, используя этот список, тоесли я удаляю какой-либо элемент из TreeView, возникает ошибка неверного указателя !!

Даже если все указатели расположены, MyRecPtr по-прежнему указывает на место в памяти, и Node.Data тоже!

1 Ответ

0 голосов
/ 02 января 2019

Ваш код дает сбой, потому что вы освобождаете одну и ту же память дважды, потому что вы не определили четкое владение вашими экземплярами записей.

Ваши списки ListaDePonteiros и ListaDeObjectos являются избыточными и могут быть удалены,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 (так как нет необходимости поддерживать два списка, отслеживающих одинаковые значения).Вам просто нужно решить, хотите ли вы, чтобы ListaDeObjectos или Tree1 владел записями, которые вы выделяете:

  • Если 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.OnDeletion, чтобы синхронизировать Tree1 и ListaDeObjectos при удалении отдельных узлов:

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() свой экземпляр записи, убедитесь, что вы набрали raw указатели на PMyRec, иначе компилятор не будет корректно завершать элементы записи, что приведет к утечке памяти.

...