среда, 6 октября 2010 г.

Как правильно уничтожать модальную форму. Release vs Free

Модальные формы довольно распространены в Delphi. Их основное удобство заключается в легкости контроля жизненного цикла формы (Create - ShowModal - Free), плюс мгновенное получение результата выбора пользователя (ModalResult). Не буду говорить о том, что приложение, перегруженное модальными формами отталкивает пользователя, это отдельная тема.

Однако довольно часто встречается, что программисты такие формы освобождают не совсем корректно, используя метод Release, вместо Free. В простых случаях это не страшно, в более сложных может привести иногда к странным "глюкам", а иногда и к AV (Access Violation).

Для начала разберёмся, что такое Release, как он работает и когда его надо использовать.

Метод формы Release не уничтожает форму немедленно, а посылает самой же себе сообщение CM_RELEASE. Посылает через PostMessage, т.е. ставит сообщение в очередь и возвращает управление программе. Затем, обработав все сообщения и получив CM_RELEASE, форма вызывает себе Free.

Вот что об этом методе написано в справке Delphi (в переводе на русский):

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

Здесь же сказано:

Обработчикам событий формы или компонентов формы стоит использовать Release вместо Free, чтобы уничтожение формы не привело к ошибкам доступа к памяти.

Казалось бы всё понятно: Release нужен чтобы избежать возможных AV при освобождении формы. Однако не все программисты внимательны, и берут за правило: "формы всегда уничтожать только через вызов метода Release". Это правило не верно, и я объясню почему.

Вернёмся к модальным формам и рассмотрим такой пример.

procedure TMyForm.OnButtonClick(Sender: TObject);
var
  SomeObject: TSomeObject;
begin
  SomeObject := TSomeObject.Create;
  try
    with TSomeForm.Create(Self) do
    try
      SomeProperty := SomeObject; // SomeProperty - свойство только что созданной формы
      ShowModal;
    finally
      Release;  // <- Обратим внимание на эту строку!
    end;
  finally
    SomeObject.Free;
  end;
end;

В этом примере модальная форма работает с неким SomeObject, за создание и уничтожение которого отвечает вызывающая сторона. Вызывающая же сторона (по отношению к модальной форме) отвечает и за создание и за уничтожение самой модальной формы. На первый взгляд всё красиво.

Однако, если в обработчике OnDestroy модальной формы попытаться обратиться к SomeObject, произойдёт AV. Заменив же Release в строке 12 на привычный нам Free получится избежать подобных ошибок.

Для сторонников метода Release хочу отметить, что вызов Free в данном примере не противоречит справке (где расписано, в каких именно местах стоит использовать Release). Почитайте справку ещё раз и обратите внимание на слова "обработчики событий формы" и "обработчики событий компонент формы".

3 коммент.:

Анонимный комментирует...

Спасибо. Век живи - век учись.

Алексей Тимохин комментирует...

Спасибо!
Несколько раз пробовал использовать Release по правилу "Release должен быть всегда лучше чем Free", сталкивалася с ошибками и снова возвращался к Free.

Анонимный комментирует...

Ставим Release в событии onClose, а в приведенном обработчике ничего писать не надо

Отправить комментарий

.

.