delphi – 在树视图节点内存储接口指针

前端之家收集整理的这篇文章主要介绍了delphi – 在树视图节点内存储接口指针前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
我试图在TTreeNode.Data属性下的树视图中存储接口指针.虽然我能够存储一个接口指针(Node.Data:= Pointer(MyInterface);)但它似乎没有相反的方式(MyInterface:= ISomeInterface(Node.Data);).总是没有.

我也试图使用手动引用计数,正如我在another question中看到的那样.然而,它仍然是零,现在给内存泄漏.

//Clears tree view and adds drive letters
procedure TfrmMain.cmdRefreshBrowseClick(Sender: TObject);
var
  Arr,O: ISuperObject;
  X: Integer;
  N,C: TTreeNode;
begin
  //First clear all items and release their interface refs
  for N in tvBrowse.Items do begin
    O:= ISuperObject(N.Data);
    O._Release;
  end;
  tvBrowse.Items.Clear;
  Arr:= ListDirectory(''); //Returns ISuperObject array listing drives
  for X := 0 to Arr.AsArray.Length-1 do begin
    O:= Arr.AsArray.O[X];
    N:= tvBrowse.Items.Add(nil,O.S['drive']+':\ ['+O.S['type']+']'); //Add root node
    N.Data:= Pointer(O); // Assign interface pointer to node data
    O._AddRef; //Manually increment interface reference count
    C:= tvBrowse.Items.AddChild(N,''); //Add a fake child node
  end;
end;

procedure TfrmMain.tvBrowseExpanding(Sender: TObject; Node: TTreeNode;
  var AllowExpansion: Boolean);
var
  N,C: TTreeNode;
  P,A,O: ISuperObject;
  X: Integer;
begin
  //Check first node if it's a fake node
  N:= Node.getFirstChild;
  if N.Text = '' then begin //if first node is a fake node...
    P:= ISuperObject(Node.Data); // <-- P always comes out nil here???
    N.Delete; //Delete first "fake" node
    //Get child files/folders
    if Node.Parent = nil then //If root (drive) node...
      A:= ListDirectory(P.S['drive']+':\') //Returns ISuperObject array listing files/folders
    else
      A:= ListDirectory(P.S['name']); //Returns ISuperObject array listing files/folders
    for X := 0 to A.AsArray.Length-1 do begin
      O:= A.AsArray.O[X];
      C:= tvBrowse.Items.AddChild(N,O.S['name']); //Add child node
      C.Data:= Pointer(O); //Assign interface pointer to node data
      O._AddRef; //Manually increment reference count
    end;
  end;
end;

这样做的适当方法是什么?

解决方法

基本上你正是这样做的.您的演员表是合理的,并且您理解需要执行手动引用计数,因为您在Pointer类型的字段中保存引用,该字段不执行引用计数.
P := ISuperObject(Node.Data);

如果P被赋值为nil,则意味着Node.Data等于nil.没有什么可说的了.据推测,数据为零是一些相当平凡的原因,但这与你的投射方式无关.

看看你的代码,我会批评它将所有不同的问题混合在一起.如果您可以在各个不同方面之间保持一定程度的隔离,您会发现这项任务非常简单.

使生活更简单的一种方法是避免使用无类型指针Data.而是使用可以执行正确引用计数的自定义节点类型:

type
  TMyTreeNode = class(TTreeNode)
  private
    FIntf: IInterface;
  property
    Intf: IInterface read FIntf write FIntf;
  end;

您需要处理树视图的OnCreateNodeClass事件以获取控件以创建节点类.

procedure TForm1.TreeView1CreateNodeClass(Sender: TCustomTreeView;
  var NodeClass: TTreeNodeClass);
begin
  NodeClass := TMyTreeNode;
end;

现在,只要树视图控件创建节点实例,它就会创建一个类型为TMyTreeNode的实例.这恰好有一个字段来包含你的界面.我在这里输入了它作为IInterface,但你可以使用更符合你需求的更具体的界面.当然,您可以将任何您喜欢的功能添加自定义节点类型中.

对此的温和绑定是您需要将TTreeNode(由底层树视图控件返回)的节点引用强制转换为TMyTreeNode,以便获得对interface属性的访问权限.但是,在我看来,这个绑定非常值得,因为您可以正确地依赖编译器来管理生命周期,因此忘记了代码的所有方面.这将使您专注于您的程序而不是繁琐的样板.继续沿着您当前所处的路径看起来像内存泄漏和访问冲突的配方.让编译器管理事物,你可以确保避免任何这样的陷阱.

猜你在找的Delphi相关文章