【问题标题】:VirtualTreeView Memory Leak with Objects对象的 VirtualTreeView 内存泄漏
【发布时间】:2015-03-06 14:38:15
【问题描述】:

我在这个post 中使用Cosmin Prund 提供的代码,因为它符合我的需要,但是我经常遇到内存泄漏,我无法弄清楚如何释放包含@ 的TNode 对象的节点987654326@ 反过来,最后一个也可以包含TNode,也可以包含TObjectList 等等......我虽然是某种递归,

据我所知,根据此link 释放VirtualTreeView 中的节点,该节点需要在OnFreeNode 事件中进行验证并最终确定,此代码返回无效的指针操作,当然还有内存泄漏报告:

procedure TfrmFichePermission.VSTFreeNode(Sender: TBaseVirtualTree;
  Node: PVirtualNode);
var
  AObject:TObject;
  ANode: TNode;
begin
  AObject := TObject(VST.GetNodeData(Node)^);
  ANode := TNode(AObject);
  ANode.Free;
end;

这是重现内存泄漏的完整示例

unit Unit1;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, VirtualTrees, System.Generics.Collections,
  Vcl.StdCtrls;

type
  TNode = class;

  TForm1 = class(TForm)
    VST: TVirtualStringTree;
    Button1: TButton;
    procedure VSTGetNodeDataSize(Sender: TBaseVirtualTree;
      var NodeDataSize: Integer);
    procedure VSTGetText(Sender: TBaseVirtualTree; Node: PVirtualNode;
      Column: TColumnIndex; TextType: TVSTTextType; var CellText: string);
    procedure Button1Click(Sender: TObject);
  private
    procedure AddNodestoTree(ParentNode: PVirtualNode; Node: TNode);
  public
    { Public declarations }
  end;

  TNode = class
  public
    Name: string;
    VTNode: PVirtualNode;
    Sub: TObjectList<TNode>;
    constructor Create(aName: string);
    destructor Destroy; override;
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

constructor TNode.Create(aName:string);
begin
  Name := aName;
  Sub := TObjectList<TNode>.Create;
end;

destructor TNode.Destroy;
begin
  Sub.Free;
end;

procedure TForm1.AddNodestoTree(ParentNode: PVirtualNode; Node: TNode);
var SubNode: TNode;
    ThisNode: PVirtualNode;
begin
  ThisNode := VST.AddChild(ParentNode, Node); // This call adds a new TVirtualNode to the VT, and saves "Node" as the payload

  Node.VTNode := ThisNode; // Save the PVirtualNode for future reference. This is only an example,
                           // the same TNode might be registered multiple times in the same VT,
                           // so it would be associated with multiple PVirtualNode's.

  for SubNode in Node.Sub do
    AddNodestoTree(ThisNode, SubNode);
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  Root: TNode;
begin
  ReportMemoryLeaksOnShutdown := True;
  VST.Clear;
  //
  Root := TNode.Create('Test1');
  Root.Sub.Add(TNode.Create('Test2'));
  Root.Sub.Add(TNode.Create('Test3'));
  Root.Sub[1].Sub.Add(TNode.Create('Test4'));
  Root.Sub[1].Sub.Add(TNode.Create('Test5'));
  AddNodesToTree(nil, Root);
  //
  Root := TNode.Create('Test1');
  Root.Sub.Add(TNode.Create('Test2'));
  Root.Sub.Add(TNode.Create('Test3'));
  Root.Sub[1].Sub.Add(TNode.Create('Test4'));
  Root.Sub[1].Sub.Add(TNode.Create('Test5'));
  AddNodesToTree(nil, Root);
  //
end;
procedure TForm1.VSTGetNodeDataSize(Sender: TBaseVirtualTree;
  var NodeDataSize: Integer);
begin
  NodeDataSize := SizeOf(Pointer);
end;

procedure TForm1.VSTGetText(Sender: TBaseVirtualTree; Node: PVirtualNode;
  Column: TColumnIndex; TextType: TVSTTextType; var CellText: string);
var
    AObject:TObject;
    ANode: TNode;
begin
  AObject := TObject(VST.GetNodeData(Node)^);
  ANode := TNode(AObject);
  CellText := ANode.Name;
end;

end.

内存泄漏报告:

【问题讨论】:

  • TObjectList 默认拥有对象。
  • 为什么先转换成 TObject 再转换成 TNode?为什么不直接到 TNode?我们怎么知道 TNode 是什么?如果那里有错误怎么办?为什么只发布少量代码。为一个完整并展示了问题的控制台应用程序发布一个简短的 .dpr 是很容易的。如果你这样做了,你会在 10 分钟内得到答案。
  • @David Heffernan 你知道我正要问同样的问题,关于为什么投射 twise TObject 而不是 TNode :) 对于示例,它与我在上面提到的帖子 + OnFreeNode 事件的代码相同所以我只发布额外的代码......
  • 您真正的问题不是内存泄漏,而是无效的指针操作。解决这个问题,您的内存泄漏也很可能会得到解决。
  • 我们没有收到有关问题编辑的通知

标签: delphi memory-leaks virtualtreeview


【解决方案1】:

Cosmin 的代码不打算让树视图节点拥有TNode 对象。我认为在他的帖子中,您应该保留Root 对象并在树被破坏后将其销毁。

在 Cosmin 的代码中,TNode 对象归包含它们的对象列表所有。一直到由创建它的任何人拥有的根节点。你也可以这样做。您必须记住根对象,并在销毁树视图节点时停止销毁 TNode 对象。

如果您想让树视图拥有TNode 对象,那么您可以这样做。但是你需要清楚所有权。您不能像当前那样拥有拥有对象的树视图对象列表。如果树视图将成为所有者,那么您需要在对象列表中将OwnsObjects 设置为False。或者更好地切换到TList&lt;TNode&gt;

因此,总而言之,您当前的代码为每个 TNode 对象提供了两个所有者。树视图节点和拥有对象列表。对象需要只有一个所有者。您需要在两个选项之间进行选择。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-10-31
    • 2012-09-12
    • 2011-01-26
    • 2011-12-09
    • 1970-01-01
    • 2010-12-20
    • 2011-03-08
    • 1970-01-01
    相关资源
    最近更新 更多