Как при закрытии формы ее же и уничтожить?

Вопросы программирования и использования среды Lazarus.

Модератор: Модераторы

Сообщение pda » 17.08.2006 21:14:38

tria писал(а):Может я какой-то неправильный, но у меня и в Делфи применение Free к уже уничтоженному объекту ВСЕГДА приводило к ошибке.

Не заниливались просто... :-( Для этого потом FreeAndNil и сделали.
Аватара пользователя
pda
постоялец
 
Сообщения: 303
Зарегистрирован: 27.05.2005 19:59:53

Сообщение Sergei I. Gorelkin » 17.08.2006 21:24:11

Вообще, реализовать принцип "жить долго и умереть вовремя" при большом количестве компонентов - является довольно нетривиальной задачей. Не имея полного текста программы, тут сложно что-то конкретное сказать. Но, как показывает практика, приходится использовать методы TComponent.Notification, TComponent.FreeNotification, и проверки типа if csDestroying in ComponentState then ..., а также следить за тем, какой Owner передается в конструкторы компонентов и вызывается ли из них inherited Create(aOwner).

Насчет имен - не стоит особо переживать - со временем жизни компонентов они не связаны, а попытка задать повторяющееся имя в пределах одного контейнера сразу же выкинет исключение.
Аватара пользователя
Sergei I. Gorelkin
энтузиаст
 
Сообщения: 1395
Зарегистрирован: 24.07.2005 14:40:41
Откуда: Зеленоград

Сообщение tria » 18.08.2006 10:56:39

Нашел!
В главной форме приложения в OnClose закомментарил код:

Код: Выделить всё
For i:=0 to ComponentCount - 1 do begin
   If not Components[i].InheritsFrom(TForm) Then Continue;
   fm:=(Components[i] as TForm);
   If fm.Visible Then fm.Close;
end;


и все заработало!
tria
постоялец
 
Сообщения: 401
Зарегистрирован: 03.04.2006 11:24:10

Сообщение Сергей Смирнов » 18.08.2006 11:16:07

tria писал(а):Нашел!
В главной форме приложения в OnClose закомментарил код:

Код: Выделить всё
For i:=0 to ComponentCount - 1 do begin
   If not Components[i].InheritsFrom(TForm) Then Continue;
   fm:=(Components[i] as TForm);
   If fm.Visible Then fm.Close;
end;


и все заработало!

Обычно циклы по коллекциям объектов начинают крутить с конца. Если немного подумаете, поймёте почему :)
Аватара пользователя
Сергей Смирнов
энтузиаст
 
Сообщения: 595
Зарегистрирован: 28.04.2005 13:23:25
Откуда: Москва

Сообщение Sergei I. Gorelkin » 18.08.2006 11:55:20

Имеет место быть типичный пример двойного освобождения. При вызове Close для дочерней формы запрос на ее удаление ставится в очередь, а после завершения OnClose главной формы она удаляется со всеми дочерними формами. Потом выполняются запросы из очереди (их ведь оттуда никто не убирал), и пытаются еще раз удалить формы.

Во-первых, проблемы не было бы, если бы дочерние формы имели владельцем Application, а не главную форму (искать их пришлось бы в Application.Components, но формы гораздо удобнее искать в Screen.Forms - там ничего кроме форм нет и лишние проверки не нужны).

Во-вторых, после закрытия главной формы завершается приложение, и все дочерние формы по-любому уничтожаются, поэтому явные вызовы Close не имеют смысла. Если при закрытии нужно что-то сохранить, это нужно делать в обработчике OnCloseQuery - оттуда же, если требуется, можно предотвратить закрытие формы и приложения в целом. Или, если сохранять не нужно, зато нужно кого-то еще прибить - то в обработчике OnDestroy - он всегда вызывается.

А с занулением указателей эта проблема связана весьма косвенно (т.е. связана конечно, но до указателей, которые нужно занулить, нам не дотянуться - они в недрах LCL).
Аватара пользователя
Sergei I. Gorelkin
энтузиаст
 
Сообщения: 1395
Зарегистрирован: 24.07.2005 14:40:41
Откуда: Зеленоград

Сообщение tria » 18.08.2006 14:51:20

Обычно циклы по коллекциям объектов начинают крутить с конца. Если немного подумаете, поймёте почему :)


Вот потому то в цикле стоит Close а не Free. :)
С Close на Делфях все работало нормально.
tria
постоялец
 
Сообщения: 401
Зарегистрирован: 03.04.2006 11:24:10

Сообщение tria » 18.08.2006 14:53:08

Если убрать закрытие дочерних форм в OnClose главной, то почему-то не вызываются OnCloseQuery дочерних форм. Вот для чего я когда-то это делал...
Может правильнее сделать то же самое в OnCloseQuery главной формы?
tria
постоялец
 
Сообщения: 401
Зарегистрирован: 03.04.2006 11:24:10

Сообщение Сергей Смирнов » 18.08.2006 15:09:56

tria писал(а):Вот потому то в цикле стоит Close а не Free. :)
С Close на Делфях все работало нормально.

Кого обмануть хотите? :) Если в форме есть
Код: Выделить всё
CloseAction := caFree;

то форма всё равно уничтожается и список форм перестраивается. Как оно на дельфях умудрялось работать ума не приложу :?
Аватара пользователя
Сергей Смирнов
энтузиаст
 
Сообщения: 595
Зарегистрирован: 28.04.2005 13:23:25
Откуда: Москва

Сообщение tria » 18.08.2006 15:30:21

Специально глянул в исходник на Делфи. Там было следующее:

Код: Выделить всё
For i:=0 to MDIChildCount-1 do begin
   MDIChildren[i].Close;


Работало, сбоев я не замечал.
Соответственно, на Лазаре нет МДИ, поэтому при переносе переделалось на то, что выше.

А обманывать мне кого-либо смысла нету...

Тут еще вопрос, вычисляется ли MDIChildCount-1 один раз при начале цикла, или каждый раз после прохода.
В давнишней теории говорилось, что один раз до начала цикла для оптимизации. Но что-то мне помнится, что где-то у меня вычислялось при каждом проходе. Но могу и ошибаться.
tria
постоялец
 
Сообщения: 401
Зарегистрирован: 03.04.2006 11:24:10

Сообщение Сергей Смирнов » 18.08.2006 15:37:30

tria писал(а):А обманывать мне кого-либо смысла нету...
:)

tria писал(а):Тут еще вопрос, вычисляется ли MDIChildCount-1 один раз при начале цикла, или каждый раз после прохода.
В давнишней теории говорилось, что один раз до начала цикла для оптимизации. Но что-то мне помнится, что где-то у меня вычислялось при каждом проходе. Но могу и ошибаться.
Вроде как один раз в начале цикла. По крайней мере, закладываться на какое-то другое поведение я бы не рискнул.
Аватара пользователя
Сергей Смирнов
энтузиаст
 
Сообщения: 595
Зарегистрирован: 28.04.2005 13:23:25
Откуда: Москва

Сообщение Sergei I. Gorelkin » 18.08.2006 15:44:27

tria писал(а):Если убрать закрытие дочерних форм в OnClose главной, то почему-то не вызываются OnCloseQuery дочерних форм. Вот для чего я когда-то это делал...
Может правильнее сделать то же самое в OnCloseQuery главной формы?


Так и следовало бы вызывать CloseQuery дочерних форм из CloseQuery главной - зачем ходить в обход?. Кстати, в MDI-приложениях на Дельфи это делается автоматически.

Сергей Смирнов писал(а):...то форма всё равно уничтожается и список форм перестраивается. Как оно на дельфях умудрялось работать ума не приложу


Легко :) Close с Action=caFree не вызывает уничтожения формы, а только ставит запрос на уничтожение в очередь. В Дельфи это происходило с помощью PostMessage, поэтому форма оставалась бы жива до выборки этого сообщения из очереди. Но! при закрытии главной формы происходит вызов Application.Terminate, после которого цикл выборки сообщений прерывается, и форма уничтожается уже не из-за вызова Close, а за счет уничтожения ее владельца.
В LCL все иначе - поскольку системная очередь сообщений есть не везде, там свой отдельный платформенно-независимый механизм обработки асинхронных событий, который явно выполняет все поставленные в очередь запросы даже после Application.Terminate.
Аватара пользователя
Sergei I. Gorelkin
энтузиаст
 
Сообщения: 1395
Зарегистрирован: 24.07.2005 14:40:41
Откуда: Зеленоград

Сообщение tria » 18.08.2006 15:58:00

Специально попробовал:
Код: Выделить всё
procedure TForm1.BitBtn1Click(Sender: TObject);
var i:integer;
    m:integer;
  Function f():integer;
  begin
    m:=m+1;
    Result:=m;
  End;
begin
  m:=1;
  For i:=1 to f() do ShowMessage(IntToStr(i));
end;


Цикл выполнился 2 раза.

Тут мне вспомнилась такая вестчь. Где-то давно писалось, что циклы такого рода на самом деле выполняются в обратном порядке.
Смысл был в увеличении скорости. При вычитании при установлинеии счетчика в 0 выставлялся флаг нуля в регистре флагов и ненадо было делать еще одно сравнение. Ассемблерные комманды уже не помню...

Согласен с стем, что правильнее было бы идти в обратном порядке.
Предлагаю это ответвление темы закрыть.

Меня больше интересует, что делать с OnCloseQuery.
tria
постоялец
 
Сообщения: 401
Зарегистрирован: 03.04.2006 11:24:10

Сообщение tria » 18.08.2006 16:02:38

Sergei I. Gorelkin как всегда расставил все по своим местам.
Большое спасибо.
tria
постоялец
 
Сообщения: 401
Зарегистрирован: 03.04.2006 11:24:10

Сообщение Сергей Смирнов » 18.08.2006 16:23:39

Sergei I. Gorelkin писал(а):Легко :) Close с Action=caFree не вызывает уничтожения формы, а только ставит запрос на уничтожение в очередь. В Дельфи это происходило с помощью PostMessage, поэтому форма оставалась бы жива до выборки этого сообщения из очереди. Но! при закрытии главной формы происходит вызов Application.Terminate, после которого цикл выборки сообщений прерывается, и форма уничтожается уже не из-за вызова Close, а за счет уничтожения ее владельца.
В LCL все иначе - поскольку системная очередь сообщений есть не везде, там свой отдельный платформенно-независимый механизм обработки асинхронных событий, который явно выполняет все поставленные в очередь запросы даже после Application.Terminate.
Дотошный Вы человек. Как говорится, "снимаю шляпу".
Аватара пользователя
Сергей Смирнов
энтузиаст
 
Сообщения: 595
Зарегистрирован: 28.04.2005 13:23:25
Откуда: Москва

Сообщение Sergei I. Gorelkin » 18.08.2006 17:35:50

Сергей Смирнов писал(а):Дотошный Вы человек. Как говорится, "снимаю шляпу".


Спасибо :) Не от хорошей жизни, однако, - пока что ни VCL, ни тем более LCL не позволяют просто решать поставленные задачи, без разного там копания в потрохах...
Аватара пользователя
Sergei I. Gorelkin
энтузиаст
 
Сообщения: 1395
Зарегистрирован: 24.07.2005 14:40:41
Откуда: Зеленоград

Пред.

Вернуться в Lazarus

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 56

Рейтинг@Mail.ru