我试图在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;
这样做的恰当方法是什么?
答案 0 :(得分:7)
基本上你正确地这样做了。您的演员表是合理的,并且您理解需要执行手动引用计数,因为您在Pointer
类型的字段中保留了引用,该字段不执行引用计数。
P := ISuperObject(Node.Data);
如果为P
分配了值nil
,则表示Node.Data
等于nil
。没有什么可说的了。据推测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
(由基础树视图控件返回)的节点引用转换为{{1}},以便获得对interface属性的访问权限。但是,在我看来,这个绑定非常值得,因为您可以正确地依赖编译器来管理生命周期,因此忘记了代码的所有方面。这将使您专注于您的程序而不是繁琐的样板。继续沿着您当前所处的路径看起来像内存泄漏和访问冲突的配方。让编译器管理事物,你可以确保避免任何这样的陷阱。