delphi - recorrido - podar arbol en c
Almacenamiento del puntero de la interfaz dentro de nodos de vista de árbol (1)
Estoy intentando almacenar punteros de interfaz en una vista de árbol en propiedades TTreeNode.Data
. Aunque puedo almacenar un puntero de interfaz ( Node.Data := Pointer(MyInterface);
) no parece funcionar al revés ( MyInterface := ISomeInterface(Node.Data);
). Siempre sale nil
.
También intenté usar el conteo manual de referencias, como lo he visto requerido en otra pregunta . Sin embargo, sigue saliendo nil
y ahora da pérdidas de memoria.
//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;
¿Cuál es la forma apropiada de hacer esto?
Esencialmente estás haciendo esto correctamente. Sus conversiones son razonables y comprende la necesidad de realizar un recuento manual de referencias ya que mantiene la referencia en un campo de tipo Pointer
que no realiza recuentos de referencia.
P := ISuperObject(Node.Data);
Si a P
se le asigna el valor nil
significa que Node.Data
es igual a nil
. No hay nada más que decir. Es de suponer que hay una razón un tanto mundana para que Data
sea nil
, pero no tiene nada que ver con la forma en que estás emitiendo.
Al mirar tu código, lo criticaría por mezclar todas las preocupaciones juntas. Encontrará esta tarea mucho más fácil si puede mantener un grado de aislamiento entre los diferentes aspectos.
Una forma de simplificar la vida es evitar utilizar los Data
puntero sin tipo. En su lugar, use un tipo de nodo personalizado que pueda realizar el recuento de referencias adecuado:
type
TMyTreeNode = class(TTreeNode)
private
FIntf: IInterface;
property
Intf: IInterface read FIntf write FIntf;
end;
Tendrá que manejar el evento OnCreateNodeClass
de la vista de árbol para obtener el control para crear su clase de nodo.
procedure TForm1.TreeView1CreateNodeClass(Sender: TCustomTreeView;
var NodeClass: TTreeNodeClass);
begin
NodeClass := TMyTreeNode;
end;
Ahora, cada vez que el control tree view crea una instancia de nodo crea uno de tipo TMyTreeNode
. Que tiene un campo para contener tu interfaz. Lo he escrito como IInterface
aquí, pero usaría la interfaz más específica que se ajuste a sus necesidades. Y, por supuesto, puede agregar cualquier capacidad que desee a su tipo de nodo personalizado.
El enlace leve a esto es que necesita convertir las referencias de nodo de TTreeNode
(como lo devuelve el control de vista de árbol subyacente) a TMyTreeNode
para obtener acceso a la propiedad de interfaz. Sin embargo, este enlace vale la pena, desde mi punto de vista, ya que puede confiar en el compilador para el tiempo de vida administrado correctamente, y así olvidar todo acerca de ese aspecto del código. Esto le permitirá concentrarse en su programa en lugar de aburrido repetitivo. Continuar por el camino en el que se encuentra actualmente parece una receta para fugas de memoria y violaciones de acceso. Haga que el compilador administre las cosas y puede estar seguro de evitar tales trampas.