2014-09-01 28 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