-3

I'm struggling with interfaces in Delphi. This question might be trivial, but I am new to Delphi, so please excuse.

I have a TreeView with customized nodes, which hold an interface to an object (essentially, just like it is proposed here: Storing interface pointer inside tree view nodes).

The problem is, once I delete a node (in order to redraw the treeview) and set the interface variable to nil (freeing won't do with interfaces for some reason I haven't fully understood), the weirdest thing happens:

In my object, which contains a list, an integer and a string variable, the string and list will be set empty, while the integer remains the same.

I can't explain this. Does anybody know a workaround, or the possible reason for this behavior? BTW, I am using Delphi 10.2 Tokyo.

Here's my quite unspectacular destroy method:

myNode.destroy;
begin
  intf:= nil;// intf holds the interface to the object
end;

Edit: this is a simplified version of my code:

The object I'm referring to: (I have several similar classes which look like obj but are slightly different and I don't know which one will be stored in the interface, but all share these variables)

Obj = class(InterfacedObject, IMyinterface)
  count: integer;  //this remains the same
  children: array of ChildObj;  //this will be emptied
  name: string;  //this will be set to ''
  procedure addChild;
  procedure IMyInterface.add = addChild;
end;

My customized treeNode:

MyNode = class(TTreeNode)
  Intf: IMyinterface;
  destructor destroy; override;
end;

Inside my class manages the TreeView:

MyForm.ReloadTree;
begin
  if myTreeView.Items.Count > 0 then
  begin
    myTreeView.Items.Clear;
  end
  for I:= 0 to RootObj.Count-1 do
  begin
    myTreeView.Items.AddChild(MyTreeview.Items[0], RootObj.Children[i].name);
    (myTreeView.Items[0][i] as MyNode).Intf := Intf(RootObj.Children[i]);
    //I will proceed iterating over all children and their children, doing 
    //the same process, a level higher in the treeView
    //...
  end;
end;
zink
  • 1
  • 2
  • oh, and my goal is to not alter the object at all, since I still need it – zink Sep 23 '17 at 13:49
  • Just delete the node. Internal data are being "nilled" in `TTreeNode` destructor (at least in VCL). That should decrement reference count of your interfaced object and release it. – Victoria Sep 23 '17 at 13:52
  • thanks for your quick response :) I tried that, but the result is just the same. I figured if I override the destructor whatever happens internally that causes this behaviour might not happen. But it does – zink Sep 23 '17 at 13:56
  • 4
    Please post an [mcve] showing exactly how *your code* (not someone else's code) is creating your nodes and assigning interfaces to them. – Remy Lebeau Sep 23 '17 at 14:02
  • 1
    We need a [mcve] – David Heffernan Sep 23 '17 at 15:45
  • 1
    Please paste your code into the q properly, and don't put nonsense like `MyForm.ReloadTree; begin [...]` which won't even compile, never mind execute. – MartynA Sep 23 '17 at 16:48
  • Apologies if my code doesn't satisfy your demands. It should compile now. I don't have so much more to add though. I feel like my problem simply requires to restructure it. @David Heffernan thank you very much for that beautiful solution to the problem I have linked, btw. – zink Sep 23 '17 at 20:46
  • The fake code is not helpful. Why can't you fix it and provide a [mcve] as you have been asked? – David Heffernan Sep 25 '17 at 08:07

1 Answers1

0

in my object, which contains a list, an integer and a string variable, the string and list will be set empty, while the integer remains the same.

This is perfectly normal behavior. Strings and interfaces are compiler-managed types. Integers are not. When an object is destructed, compiler-managed data members are automatically deallocated as needed, which in the case of strings and interfaces involves nil'ing the pointers to their referenced data. The containing object itself is not zeroed out completely, so non-managed types, like integers, are not overwritten in memory.

Now, that being said, I see some bugs in your ReloadTree() procedure.

  1. Your for loop is exceeding the upper bound of the RootObj.Children[] list.

  2. When calling AddChild(), the second parameter is a string. You are passing RootObj.Children[i] in that parameter. But, in the next statement, you are type-casting the same RootObj.Children[i] value to an interface when assigning the MyNode.Intf field. A string is not an interface. So, what exactly does RootObj.Children[] contain - strings or interfaces?

  3. When assigning the MyNode.Intf field, you are always accessing the first node in the TreeView, instead of the newly added node.

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • Thank you, this explains why it happens. Is there anyway I can prevent the 'zeroing out' when destroying the interface? – zink Sep 23 '17 at 14:38
  • @zink no, and why would you want to anyway? What are you really trying to solve exactly? – Remy Lebeau Sep 23 '17 at 15:10
  • oh yeah, you're right about the bugs, I'm sorry I didn't have my code available so I just quickly retyped what I knew by memory. The actual code avoids all of these problems :) – zink Sep 23 '17 at 20:10
  • and what I am trying to do is let the user manage the data, which is very much structured like a tree, by adding, deleting and editing obj objects. I thought I could do that with a treeview. The problem is that I sometimes I need to redraw my data, because I want to change the color of a branch when some of the data it's representing is corrupted. I haven't found a way to manage that, other than deleting and redrawing the corresponding nodes. Which will lead to my object being altered as discribed. – zink Sep 23 '17 at 20:19