Подтвердить что ты не робот

Заблуждение владельцев Delphi

Я всегда думал, что владелец несет ответственность за уничтожение визуальных элементов управления и что я могу вручную управлять уничтожением, если я передаю nil в качестве владельца.

Рассмотрим следующий пример:

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

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

Я бы ожидал, что эта кнопка создаст утечку памяти, но это не так, и на самом деле вызывается деструктор TButton.

Дальнейшие исследования показали, что деструктор TWinControl содержит следующий фрагмент кода:

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

который выглядит так, будто он уничтожает дочерние компоненты (те, у которых Parent установлен сам контроль).

Я не ожидал, что родительский элемент управления уничтожит элемент управления. Может ли кто-нибудь объяснить, почему это происходит? И кто разрушает объект, если я передаю владельцу?

4b9b3361

Ответ 1

почему это происходит?

Это имеет смысл и по дизайну. Как вы думаете, что должно произойти с потерянными дочерними элементами, когда родитель уничтожается? Должны ли они внезапно начать плавать как окна верхнего уровня? Возможно нет. Должны ли они переопределяться на другой контроль? Какой?

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

Parent, если он назначен и освобождается первым. TWinControl переопределяет деструктор TComponent, чтобы освободить его дочерние элементы управления first (унаследованный деструктор вызывается только позже). Ребенок управляет уведомлять свои Owner об уничтожении, который удаляет их из списка принадлежащих ему компонентов. Поэтому владелец не пытается снова освободить ваш объект позже в своем деструкторе.

Если Parent - это тот же объект, что и Owner, то также применяется и выше.

Если Parent и Owner - два разных объекта, и вы сначала освобождаете Владельца, тогда компонент Owner освобождает все принадлежащие ему компоненты (см. TComponent деструктор). Ваш объект является потомком TControl и TControl переопределяет деструктор для вызова SetParent(nil);, который удаляет экземпляр из родительского списка дочерних элементов управления, Вот почему родитель не пытается снова освободить ваш объект позже в своем деструкторе.

Ответ 2

Самая ранняя версия, к которой я имею доступ сейчас, - это Delphi 5, а деструктор TWinControl имеет код, который вы там разместили, поэтому это поведение было на месте в течение длительного времени. И когда вы думаете об этом, это имеет смысл - Controls являются визуальными компонентами, и когда вы уничтожаете их контейнер (Parent), тогда имеет смысл уничтожать и дочерние. Деструктор TWinComponent не может позаботиться о том, что с ними делать (скрыть их?), Чтобы вернуть их в Parent.Parent? Но что, если текущий родитель - это окно верхнего уровня, то есть у него нет родителя? И т.д.). Поэтому разработчики VCL решили, что это самый безопасный вариант, чтобы избежать утечки памяти/дескриптора (особенно в тех случаях, когда выигрышные ручки, где в первые дни были премиум-класса, поэтому избегать утечки их, вероятно, были главным приоритетом). Поэтому, если вы хотите, чтобы дети остались, вы должны переустановить их, прежде чем уничтожить контейнер.

BTW. если вы передадите Владелец, то TComponent.DestroyComponents; (вызываемый TComponent.Destroy) уничтожает компонент.