2014-09-01 8 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). –

+0

Или я мог бы преобразовать каждый интерфейс и его содержимое в запись и сохранить указатель записи вместо указателя интерфейса –

+1

, если вы поместите InterfaceList, а ваш TTreeNode.Data указывает на интерфейс InterfaceItem? каждый элемент в вашем древовидном представлении - это элемент в interfacelist. – Passella

ответ

6

По существу, вы делаете это правильно. Ваши броски разумны, и вы понимаете необходимость выполнять ручной подсчет ссылок, поскольку вы держите ссылку в поле типа 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. Тем не менее, это связывание стоит того, на мой взгляд, потому что вы можете правильно полагаться на компилятор на управляемый срок службы, и поэтому забывайте об этом аспекте кода. Это позволит вам сосредоточиться на своей программе, а не на утомительном шаблоне. Продолжая путь, на котором вы сейчас находитесь, выглядит как рецепт утечек памяти и нарушений доступа. Получить компилятор для управления вещами, и вы можете быть уверены, чтобы избежать таких ошибок.

+0

Теперь это очень умно. –

Смежные вопросы