问题描述
我试图将界面指针存储在 TTreeNode.Data 属性下的树视图中。虽然我能够存储一个接口指针( Node.Data:= Pointer(MyInterface); )它似乎没有其他方式工作( MyInterface:= ISomeInterface(Node.Data); )。它总是出来 nil 。
I'm attempting to store interface pointers in a tree view under TTreeNode.Data properties. While I am able to store an interface pointer (Node.Data := Pointer(MyInterface);) it does not seem to work the other way around (MyInterface := ISomeInterface(Node.Data);). It always comes out nil.
我也试图使用手动引用计数,如我所见需要在。然而,它仍然出现 nil ,现在给出内存泄漏。
I've also attempted to use manual reference counting, as I've seen required in another question. However, it is still coming out nil and now giving memory leaks.
//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;
这样做的恰当方法是什么?
What's the appropriate way to do this?
推荐答案
基本上你正在做这个。您的演员是合理的,您了解需要执行手动参考计数,因为您在指针中不执行引用计数的字段中保留引用。
Essentially you are doing this correctly. Your casts are reasonable, and you understand the need to perform manual reference counting since you are holding the reference in a field of type Pointer which does not perform reference counting.
P := ISuperObject(Node.Data);
如果 P 被赋值为code> nil 这意味着 Node.Data 等于 nil 。没有什么可说的。据推测,数据是 nil 有一些相当平凡的原因,但与您投射的方式无关。
If P is assigned the value nil that means that Node.Data is equal to nil. There's nothing more to say. Presumably there is some rather mundane reason for Data being nil, but it's nothing to do with the way you are casting.
看看你的代码,我会批评它混合所有不同的关注在一起。如果您可以在各种不同的方面保持一定程度的隔离,您将会发现这个任务很容易。
Looking at your code, I would criticise it for mixing all the different concerns up together. You will find this task much easy if you can maintain a degree of isolation between the various different aspects.
使生活更简单的一种方法是避免使用无类型的指针数据。而是使用可以执行正确引用计数的自定义节点类型:
One way to make life much simpler is to avoid using the untyped pointer Data. Instead use a custom node type that can perform proper reference counting:
type TMyTreeNode = class(TTreeNode) private FIntf: IInterface; property Intf: IInterface read FIntf write FIntf; end;
您需要处理 OnCreateNodeClass 树状视图的事件,以获取创建节点类的控件。
You'll need to handle the OnCreateNodeClass event of the tree view to get the control to create your node class.
procedure TForm1.TreeView1CreateNodeClass(Sender: TCustomTreeView; var NodeClass: TTreeNodeClass); begin NodeClass := TMyTreeNode; end;
现在,当树视图控件创建一个节点实例时,它将创建一个类型 TMyTreeNode 。这恰好有一个字段来包含你的界面。我在这里输入了 IInterface ,但是您可以使用更符合您需求的界面。当然,你可以添加任何你想要的自定义节点类型的功能。
Now whenever the tree view control creates a node instance it creates one of type TMyTreeNode. Which happens to have a field to contain your interface. I've typed it as IInterface here, but you'd use the more specific interface that fits your needs. And of course you can add whatever capability you please to your custom node type.
这个温和的绑定是你需要从 TTreeNode (由底层树视图控件返回)到 TMyTreeNode 以获取对该接口属性的访问。但是,这种绑定在我看来是非常值得的,因为您可以依靠编译器正确管理生命周期,因此忘记了所有代码的方面。这将使您专注于您的程序,而不是繁琐的样板。继续沿着您当前的路径看起来像是内存泄漏和访问冲突的方法。获取编译器来管理事物,您可以确保避免任何此类陷阱。
The mild bind to this is that you need to cast node references from TTreeNode (as returned by the underlying tree view control) to TMyTreeNode in order to gain access to the interface property. However, this bind is well worth it in my view because you can rely on the compiler to managed lifetime correctly, and so forget all about that aspect of the code. This will allow you to concentrate on your program rather than tedious boilerplate. Continuing down the path you are currently on looks like a recipe for memory leaks and access violations. Get the compiler to manage things and you can be sure to avoid any such pitfalls.
这篇关于在树视图节点中存储接口指针的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!