2014-09-01 27 views
3

我试图在TTreeNode.Data属性下的树视图中存储接口指针。虽然我能够存储接口指针(Node.Data := Pointer(MyInterface);),但似乎不能以其他方式工作(MyInterface := ISomeInterface(Node.Data);)。它总是出来nil在树视图节点内存储接口指针

我也试过使用手动引用计数,因为我已经在another question中看到了。然而,它仍然出来nil,现在给内存泄漏。

//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; 

什么是适当的方法来做到这一点?

+1

您可以研究[TInterfaceList]的代码(http://docwiki.embarcadero.com/Libraries/en/System.Classes.TInterfaceList)。 – 2014-09-01 18:23:19

+0

或者我可以将每个接口及其内容转换为记录并存储记录指针,而不是接口指针 – 2014-09-01 18:25:15

+1

如果将一个InterfaceList和您的TTreeNode.Data指向InterfaceItem?树视图中的每个项目都是接口列表中的一个项目。 – Passella 2014-09-01 18:28:32

回答

6

基本上你是这样做的正确。您的演员阵容是合理的,并且您理解需要执行手动参考计数,因为您在Pointer类型的字段中保存参考,该字段不执行参考计数。

P := ISuperObject(Node.Data); 

如果P被分配意味着Node.Data等于nilnil。没有什么可说的了。据推测,Datanil有一些相当普通的原因,但这与你投射的方式无关。

看着你的代码,我会批评它将所有不同的问题混合在一起。如果你能保持各个不同方面之间的隔离程度,你会发现这项任务很容易。

使生活更简单的一种方法是避免使用无类型指针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。但是,在我看来,这种绑定非常值得,因为您可以依靠编译器正确地管理生命周期,因此忘记了代码的所有方面。这将允许你专注于你的程序,而不是繁琐的样板。继续沿着目前的路径看起来像是内存泄漏和访问违规的配方。让编译器管理事情,你可以确保避免任何这样的陷阱。

+0

现在很聪明。 – 2014-09-01 19:54:52