Как правильно динамически создавать / выпускать формы выполнения? - PullRequest
17 голосов
/ 10 июня 2011

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

В любом случае, возьмем в качестве примера следующее:

Form2:= TForm2.Create(nil);
try
  Form2.ShowModal;
finally
  Form2.FreeOnRelease;
end;

Я действительно думаю, что Form2.Destroy, вероятно, лучший вариант, который подводит меня к моему вопросу ..

В чем разница между звонками:

Form2.Destroy;
Form2.Free;
Form2.FreeOnRelease;

Они все выполняют одинаковую или похожую работу, если только я что-то упускаю.

А также когда следует использовать что-либо из вышеперечисленного?Очевидно, что при освобождении Объекта я понимаю это, но в некоторых ситуациях Destroy лучше подходит, чем, например, Free?

Ответы [ 5 ]

14 голосов
/ 10 июня 2011

Form2: = TForm2.Create (nil);

Это запах кода, потому что Form2, вероятно, глобальная переменная, генерируемая IDE, которая обычно содержитIDE-созданный TForm2.Скорее всего, вы захотите использовать локальную переменную и переменную с лучшим именем.Это не обязательно ошибка, просто запах кода.

Form2.Destroy против Form2.Free

Используйте Form2.Free, потому что он все равно вызывает Destroy,Вы можете CTRL + Нажать на имя (Free), чтобы увидеть его реализацию.По сути Free звонит Destroy, если Self не ноль.

Form2.FreeOnRelease

Как указано в документации , "It should not be necessary to call FreeOnRelease directly."

9 голосов
/ 10 июня 2011

Я никогда не слышал о FreeOnRelease раньше. Быстрый поиск в Google обнаружил причину. Из официальной документации:

FreeOnRelease вызывается, когда интерфейс реализован компонентом выпущен. FreeOnRelease используется внутренне и вызывает соответствующий интерфейсный метод. Так не должно быть необходимо позвонить в FreeOnRelease непосредственно.

Что касается Free против Destroy, Free - это функция безопасности. Он в основном реализован как if self <> nil then self.Destroy; и создан для безопасности использования конструкторов и деструкторов. Вот основная идея:

Если вы создаете объект и возникает необработанное исключение, вызывается деструктор. Если ваш объект содержит другие объекты, они могут или не могли быть созданы к моменту возникновения ошибки, поэтому вы не можете просто попытаться вызвать Destroy для всех из них. Но вам нужен способ убедиться, что созданные были уничтожены.

Поскольку Delphi обнуляет адресное пространство объекта перед вызовом конструктора, все, что еще не было создано, гарантированно будет nil на этом этапе. Таким образом, вы могли бы говорить if FSubObject <> nil then FSubObject.Destroy снова и снова для всех подобъектов (и если вы забудете, что вы получите нарушения доступа), или вы можете использовать метод Free, который сделает это за вас. (Это огромное улучшение по сравнению с C ++, где пространство памяти не обнуляется до вызова конструктора, что требует от вас обернуть все ваши подобъекты в интеллектуальные указатели и использовать RAII для обеспечения безопасности исключений!)

Это полезно и в других местах, и на самом деле нет причин не использовать его. Я никогда не замечал, что Free налагает какие-либо ощутимые потери производительности и повышает безопасность вашего кода, поэтому рекомендуется использовать его во всех случаях.

Сказав, что , когда речь идет конкретно о формах, есть еще одна переменная для учета в уравнении: очередь сообщений Windows. Вы не знаете, есть ли еще ожидающие сообщения для формы, которую вы собираетесь освободить, поэтому не всегда безопасно вызывать Free в форме. Для этого есть метод Release. Он отправляет сообщение в очередь, в результате чего форма освобождается, когда у нее больше нет сообщений для обработки, поэтому, как правило, это лучший способ освободить форму, которая вам больше не нужна.

6 голосов
/ 10 июня 2011

Каноническая форма:

Form := TMyForm.Create(nil);
try
  Form.ShowModal;
finally
  Form.Free;
end;

Никогда не звоните Destroy, всегда звоните Free.

FreeOnRelease - общая красная сельдь.Иногда, если есть сообщения в очереди, предназначенные для вашей формы или ее дочерних элементов, вы можете выбрать Release, хотя часто это свидетельствует о проблемах проектирования.

4 голосов
/ 10 июня 2011

Идиоматическое использование

procedure SomeProc;
var
  frm: TForm2;
begin
  frm := TForm2.Create(nil);
  try
    frm.ShowModal;
  finally
    frm.Free;
  end;
end;

или, если вы не ненавидите конструкцию with,

with TForm2.Create(nil) do
  try
    ShowModal;
  finally
    Free;
  end;

Никогда не звоните Destroy, согласно документации . На самом деле, Free в точности эквивалентен if Self <> nil then Destroy;. То есть это «безопасная» версия Destroy. Он не падает полностью, если указатель оказывается nil. [Чтобы проверить это, добавьте приватное поле FBitmap: TBitmap в свой класс формы, а затем OnCreate (например), попробуйте FBitmap.Free против FBitmap.Destroy.]

Если вы создаете форму, используя описанный выше подход, Free совершенно безопасен, если вы не делаете странные вещи в классе формы.

Однако, если вы используете CreateForm(TForm2, Form2) для создания формы и сохранения объекта формы в глобальной переменной экземпляра Form2, и вы не освобождаете ее немедленно [например, если вы хотите, чтобы окно оставалось рядом с основная форма немодальным способом в течение нескольких минут], вам, вероятно, следует использовать Release вместо Free. Из документации ,

Релиз не разрушает форму пока все обработчики событий формы и обработчики событий компонентов на форма завершилась. Выпуск также гарантирует, что все сообщения в очереди событий формы обрабатывается до выпуска формы. Любые обработчики событий для формы или ее дети должны использовать релиз вместо Бесплатно (Delphi) или удалить (C ++). провал это может вызвать доступ к памяти ошибка.

FreeOnRelease не имеет ничего общего с формами. Из документов:

Не нужно звонить FreeOnRelease напрямую.

0 голосов
/ 13 февраля 2018

другой путь - это переход к действию формы / закрытия

procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  Action := caFree;
end
...