26

I always thought that the owner is responsible for destroying visual controls and that I can manually control destruction if I pass nil as the owner.

Consider the following example:

TMyForm = class (TForm)
private
  FButton : TButton;
end;

...
FButton := TButton.Create(nil);   // no owner!!
FButton.Parent := Self;

I would expect this button to produce a memory leak but it doesn't and in fact the destructor of TButton is called.

Further investigation showed that the TWinControl destructor contains the following snippet of code:

I := ControlCount;
while I <> 0 do
begin
  Instance := Controls[I - 1];
  Remove(Instance);
  Instance.Destroy;
  I := ControlCount;
end;

which looks like it is destroying the child components (the ones with Parent set to the control itself).

I was not expecting the parent control to destroy the control. Can anybody explain why this is happening? And who is destroying the object if I pass in an owner?

jpfollenius
  • 16,456
  • 10
  • 90
  • 156
  • `TComponent.DestroyComponents` (called from destructor) does the destruction when you don't set the parent, but the owner. I never noticed that destruction also can happen in `TWinControl`, that's good to know. – Heinrich Ulbricht Aug 16 '11 at 09:00
  • 2
    Somewhat similar confusion in the question [How to detach a panel and show it in a separate window?](http://stackoverflow.com/questions/6461715/). It's explained in the 'notes' of [TControl.Parent Property](http://docwiki.embarcadero.com/VCL/en/Controls.TControl.Parent), but I find the explanation a little bit confusing (about the streaming part).. – Sertac Akyuz Aug 16 '11 at 09:10

2 Answers2

14

why this is happening?

It makes sense and it's by design. What do you think should happen to orphaned child controls when the parent is destroyed? Should they suddenly start floating around as top-level windows? Probably not. Should they be re-parented to another control? Which one?

who is destroying the object if I pass in an owner?

Parent, if it's assigned and being freed first. TWinControl overrides TComponent's destructor to free its child controls first (the inherited destructor is only called later). The child controls notify their Owner about being destroyed which removes them from its list of owned components. That's why the Owner doesn't attempt to free your object again later in its destructor.

If Parent is the same object as Owner then the above applies, too.

If Parent and Owner are two different objects, and you free the Owner first, then the Owner component frees all its owned components (see TComponent's destructor). Your object is a TControl descendant and TControl overrides the destructor to call SetParent(nil); which removes the instance from the parent's list of child controls. That's why the parent doesn't attempt to free your object again later in its destructor.

Ondrej Kelle
  • 36,941
  • 2
  • 65
  • 128
  • +1 Thanks! Very good explanation even though I would find it much more convincing if just `SetParent(nil)` would be executed for child controls. I mean, the whole component ownership principle was built for object destruction and since `TWinControl` inherits from `TComponent` I don't see why this second mechanism is necessary. Just makes the destruction process much more complex. Or do you know an example where this is **really** needed in addition to component ownership? – jpfollenius Aug 16 '11 at 11:20
  • Yes, there are Owners which are not TWinControl descendants. E.g. data modules or other custom modules. – Ondrej Kelle Aug 16 '11 at 11:22
  • I don't get your comment. Component ownership works fine in these cases, doesn't it? My question was: why is this additional destruction mechanism using the `parent` property necessary? – jpfollenius Aug 16 '11 at 11:23
  • 2
    Oh, I thought I explained it in the first part. "What should happen to orphaned child controls?" Because parentship assumes responsibility to clean up children, like ownership assumes responsibility to clean up owned components. And remember, Parent and Owner may be two different objects. – Ondrej Kelle Aug 16 '11 at 11:43
  • 2
    I might oversee something, but I don't get the point. Every object that has a `parent` property also has an `owner` property. So why should `parent` deal with object destruction at all? Why not use the existing ownership mechanism for destruction? "what should happend to orphaned child controls": why not just set the `Parent` to `nil`? – jpfollenius Aug 16 '11 at 12:07
  • _Every object that has a parent property also has an owner property._ Not true. You can assign a parent to a control at runtime, and the control may be created at runtime without an owner. – Ondrej Kelle Aug 16 '11 at 12:19
  • 1
    TOndrej: I'm **not** saying that every object that has a parent assigned also has an owner assigned! I'm just saying that each such object has the **possibility** to have an owner assigned, so I don't see a need for an additional destruction mechanism. – jpfollenius Aug 16 '11 at 12:39
  • 1
    Two responsibilities to clean up -> two destruction mechanisms. To me the need is clear. – Ondrej Kelle Aug 16 '11 at 12:44
8

The earliest version I have access right now is Delphi 5 and the TWinControl destructor has the code you posted there too, so this behaviour has been in place for a long time. And when you think about it it kind of makes sense - the Controls are visual components and when you destroy their container (Parent) then it makes sense to destroy the childs too. The destructor of the TWinComponent can't deside for you what to do with them (hide them? reparent them to Parent.Parent? But what if the current Parent is a top level window, ie it doesn't have Parent? etc). So the designers of the VCL desided that this is the safest option to take, avoiding memory/handle leaks (especially the win handles where at premium in the early days so avoiding leaking them was probably top priority). So if you want the childs to stay you should re-parent them before destroying the container.

BTW. if you pass an Owner then TComponent.DestroyComponents; (called by TComponent.Destroy) destroys the component.

ain
  • 22,394
  • 3
  • 54
  • 74
  • What prevents `Destroy` from being called twice if I pass in an owner? In this case both destructors should be executed... – jpfollenius Aug 16 '11 at 09:29
  • The reference to the child is removed from the internal list, so the second destructor won't "see" the (already destroyed) child. – ain Aug 16 '11 at 09:34
  • **But:** `TComponent` is not using the `Controls` array (containg the child objects) but the `Components` list, from which the control should not have been removed. – jpfollenius Aug 16 '11 at 09:36
  • 2
    There is notification system in the VCL which must take care of all the different scenarios so that the internal ownership lists are updated in time. Ie the TComponent's destructor calls `if FOwner <> nil then FOwner.RemoveComponent(Self);` – ain Aug 16 '11 at 09:45
  • ain: can you explain that a bit more detailed (maybe in an edit to your answer)? I can't see where `TWinControl`'s destructor does remove the control from it's owner's component list... – jpfollenius Aug 16 '11 at 10:16
  • @Smasher Since all controls descend from TComponent they all call the owner's `TComponent.RemoveComponent()` in (TComponent's) destructor (if they do have owner) and there `Remove(AComponent);` is called which removes the component from it's owner list. Does this answer your question? – ain Aug 16 '11 at 10:53