我试图在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属性的访问权限.但是,在我看来,这个绑定非常值得,因为您可以正确地依赖编译器来管理生命周期,因此忘记了代码的所有方面.这将使您专注于您的程序而不是繁琐的样板.继续沿着您当前所处的路径看起来像内存泄漏和访问冲突的配方.让编译器管理事物,你可以确保避免任何这样的陷阱.