Безопасно ли использовать Free вместо Release для модальных форм в Delphi?

В интерактивной справке Delphi сказано, что для удаления формы из памяти следует использовать Release. Однако во многих примерах для модальных форм я видел эту конструкцию:

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

Является ли Free безопасным способом уничтожить модальную форму? Как я вижу в исходном коде для ShowModal, Application.HandleMessage будет вызываться до тех пор, пока ModalResult не станет равным 0. Это причина, по которой Free не может мешать ожидающим сообщениям Windows?


person mjn    schedule 27.05.2009    source источник
comment
Связано: stackoverflow.com/questions/274523/form-release-nil   -  person mjn    schedule 19.12.2011
comment
Примечание. Существует ошибка Delphi, связанная с закрытием вторичных форм Delphi. Это приведет к сбою вашего приложения: quality.embarcadero.com/browse/RSP-33140.   -  person Z80    schedule 24.03.2021


Ответы (3)


Да, безопасно использовать Free после вызова ShowModal.

Случаи, когда вам нужно использовать Release, это когда вы находитесь в середине обработчика событий (например, OnClick), где дальнейшая обработка после события должна будет получить доступ к форме. В этом случае вызов Release вместо этого отправляет сообщение CM_RELEASE, которое не освобождает событие до тех пор, пока обработчик события не завершится и управление не вернется к насосу сообщений (ProcessMessages/Application.Run). ShowModal не возвращается до тех пор, пока обработчик события не завершится и управление не сделает его резервным копированием стека, поэтому последующий вызов Free фактически является тем же местом, где в противном случае обрабатывалось бы сообщение CM_RELEASE.

person Zoë Peterson    schedule 27.05.2009
comment
И будьте осторожны, чтобы не было никаких вызовов Application.ProcessMessages! - person Gerry Coll; 28.05.2009
comment
@GerryColl Почему? И где именно? - person Bozzy; 24.09.2015
comment
@Bozzy Если вы используете Release, а затем вызываете Application.ProcessMessages, сообщение WM_RELEASE будет обработано, и форма будет освобождена. Джерри беспокоит, если вы используете Release, чтобы отложить уничтожение, а затем случайно освобождаете форму, прежде чем вы на самом деле закончите с ней. - person Zoë Peterson; 24.09.2015

По-разному. Freeформа не вызывает обработчики событий, которые делает Release, и любые сообщения, которые могли быть отправлены в форму и поставлены в очередь, не будут обработаны. Таким образом, хотя во многих и, возможно, в большинстве случаев вызов Free (или FreeAndNil) будет работать нормально, это может привести к очень странному поведению по кажущимся случайным причинам.

Альтернативой, которую я бы предложил, является установка в событии OnClose значения caFree, например:

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

Затем вы можете написать такой код:

TMyForm.Create(nil).ShowModal;

И вам не нужно специально освобождать форму, так как она освободится сама, когда это будет сделано.

person Tim Sullivan    schedule 27.05.2009
comment
Мне нравится творчество, которое это демонстрирует, но я бы никогда не использовал его, поскольку код, показанный в вопросе, - это просто идиоматический способ сделать это - с вашим кодом я всегда задавался вопросом, правильно ли освобождается форма, когда спотыкаясь об это через несколько месяцев. - person mghie; 27.05.2009
comment
Мех, все, что ты считаешь лучшим. Однако это полностью инкапсулирует функциональность формы и снижает вероятность того, что вы забудете ее освободить. - person Tim Sullivan; 27.05.2009
comment
Установка действия на caFree просто вызывает Release. Вы должны быть осторожны, если хотите получить доступ к свойствам формы после showmodal, на случай, если что-то (сломанное?) вызовет ProcessMessages - person Gerry Coll; 28.05.2009
comment
Метод CM_Release просто вызывает Free(). Почему вы говорите, что освобождение формы не вызывает обработчики событий, которые делает Release? - person Darian Miller; 30.05.2009

Абсолютно, и вы также можете использовать подпрограмму FreeAndNil. Подпрограмма FreeAndNil освободит объект только в том случае, если он еще не равен нулю, а также установит его в ноль после освобождения. Если вы вызовете free непосредственно для объекта, который уже был освобожден, вы получите нарушение прав доступа.

MyForm := TMyForm.Create(nil); 
try 
  MyForm.ShowModal; 
finally 
  FreeAndNil(MyForm); 
end;
person skamradt    schedule 27.05.2009
comment
Небольшая придирка: Free также освобождает объект только в том случае, если он еще не равен нулю, вторая часть является важной. - person mghie; 27.05.2009
comment
Если MyForm является локальной переменной, использование FreeAndNil(), безусловно, излишне. MyForm.Free достаточно, на самом деле MyForm.Destroy будет работать так же хорошо. - person Allen Bauer; 27.05.2009
comment
+ для Аллена - чрезмерное использование FreeAndNil может скрыть ошибки, которые были бы обнаружены компилятором - он не будет жаловаться, если объект не был инициализирован (обычно условное создание). Это также не отвечает на вопрос (ДА, ЭТО ОК). - person Gerry Coll; 28.05.2009
comment
Есть ли опасность вызова Free для уже освобожденного объекта? Если нет, то не намного лучше использовать FreeAndNil. Если есть опасность, то FreeAndNil в этом случае все равно не поможет. (Помните, вы освобождаете объекты, а не переменные. MyForm — это переменная, а не объект.) - person Rob Kennedy; 28.05.2009
comment
Мое предположение заключалось в том, что MyForm была глобальной переменной, созданной редактором при первом создании модуля. Я согласен с Алленом, ЕСЛИ переменная полностью локальна, тогда .free достаточно, поскольку переменная выходит за рамки. - person skamradt; 29.05.2009