Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Delphi Ownership Confusion

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?

like image 400
jpfollenius Avatar asked Aug 16 '11 08:08

jpfollenius


2 Answers

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.

like image 78
Ondrej Kelle Avatar answered Oct 23 '22 22:10

Ondrej Kelle


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.

like image 25
ain Avatar answered Oct 24 '22 00:10

ain