Как совет, правильный способ сделать это в некоторых особых случаях - создать таймер, который присваивает переменной значение nil.
Я объясню (как-то это сложно), если вы создаете свою форму в своем собственном коде MyForm:=TMyForm.Create
и у вас есть MyFrom.Close
, это очень просто, просто добавьте MyForm:=nil
или также лучше MyForm.FreeAndNil
... но иногда ссылка не какая-либо.
Пример: вы создаете внутри процедуры в цикле множество копий одной и той же формы (или только одной), позволяете форме открыться и завершите эту процедуру, теперь ссылка на открытую форму нигде не существует, так что вы можете не назначать ноль или делать freeandnil и т. д., как обычно.
В этом случае хитрость заключается в том, чтобы использовать таймер (всего одну миллисекунду), который делает это, этому таймеру нужна ссылка, поэтому вы должны хранить в глобальном, например, ссылку на себя, все, что можно сделать на при закрытии мероприятия.
Самый простой способ сделать бесплатное (когда нигде нет ссылок) - это создать TObjectList в главной форме, чтобы он содержал все ссылки на формы, которые должны быть свободными, и определял таймер (одну миллисекунду), который будет выполняться через этот список делает freeandnil; затем в onlcose добавьте Self
в этот список и включите этот таймер.
Теперь другая часть, у вас есть нормальная форма, которая автоматически создается при запуске, но вам нужно установить ее на ноль и пересоздать ее по вашему собственному коду.
В этом случае есть глобальная точка, указывающая на эту форму, поэтому вам нужно только освободить и обнулить ее, но НЕ (я говорю это громко) в любой части внутри кода собственной формы, вы должны сделать это OUT (я говорю, если громко) формы кода.
Иногда вам нужно будет освободить форму, когда пользователь закроет ее, и она не отображается в модальном режиме, это сложный случай, но опять-таки тот же трюк действителен, при событии onclose вы включаете таймер (то есть из этой формы, обычно в основной форме) и этот таймер будет свободен и ноль. Этот интервал таймера может быть установлен как одна миллисекунда, он не будет работать до тех пор, пока форма полностью не закроется (пожалуйста, имейте в виду, что вы не используете Application.ProcessMessages, это обычно очень плохая идея).
Если вы установите для Self значение nil, free или что-либо другое в своей собственной форме, вы можете повредить память приложения (делая это совершенно небезопасно, не говоря уже о том, что это может съесть оперативную память).
Единственный способ освободить форму (и обнулить ее ссылку), форма, которая не отображается как модальная и является пользователем, закрывающим ее, - это запрограммировать триггер, который делает это после того, как форма полностью закрыта.
Я знаю о настройке действия, чтобы сделать бесплатное, но установить его на ноль другого безопасного пути нет.
Должен сказать: если вы используете таймеры в своей основной форме, запустите Enabled:=False
для всех из них в событии Onclose ... в противном случае могут произойти странные вещи (не всегда, но иногда ... условия гонки по поводу уничтожения приложения) и запуск кода на этих таймерах) и, конечно, если кто-то был включен, действуйте правильно, чтобы правильно завершить его или прервать его и т. д.
Ваш вопрос - одна из сложных вещей, которую нужно сделать ... освободить и обнулить форму, закрытую не кодом, а действиями пользователя.
Для всего остального: Подумайте, если в приложении одновременно открыто много форм, и все могут одновременно взаимодействовать с пользователем (любой является модальным), и у вас есть код, который ссылается на некоторые из них из другие ... вы должны знать, что пользователь закрыл любую форму, чтобы избежать доступа к этой форме из кода. Это не тривиально, если вы не используете таймеры.
Если у вас есть «центральная» форма (например, приложение MDI), вы можете поместить этот таймер в основную форму MDI, так что любая закрытая дочерняя форма может быть освобождена и равна нулю, трюк снова является таймером на этой главной форма.
Только и только если вы уверены, что можете освободить и обнулить все невидимые формы, у вас может быть таймер на главной форме, который проходит через все формы, и если Visible имеет значение false, тогда вызовите FreeAndNil, я считаю, что этот способ склонен к ошибки, так как если вы добавите в будущем форму, которая не должна быть освобождена, но может оставаться скрытой ... этот код будет недействительным.
Всегда помните, что если пользователь является пользователем, который закрывает форму, которая должна быть освобождена, и ноль, то в коде нет способа обнаружить и действовать, событие не запускается (после того, как форма полностью закрыта) и перед формой полностью закрыт, вы даже не должны пытаться освободить его или обнулить его ссылку, могут произойти странные вещи (более склонные к этому, если материнская плата имеет более одного сокета, также больше, если ваше приложение использует потоки и т. д.).
Итак, для многопоточных приложений (а также не многопоточных) я использую другой метод, который отлично работает и не требует таймеров, но требует двойной проверки перед каждым ThatForm.*
, трюк в том, чтобы определить булеву открытую переменную Form, например, как PleaseFreeAndNilMe
в открытом разделе формы, затем в onclose (как в последней строке) установите значение True, а в OnCreate установите значение False.
Таким образом, вы узнаете, была ли эта форма закрыта или только скрыта (чтобы скрыть форму, никогда не вызывайте close, просто вызовите hide).
Таким образом, кодирование будет выглядеть так (вы можете использовать это как сглаживатель, вместо того, чтобы определять формы, так как TForm определяет их как TMyform, или, что еще лучше, использовать взлом как type TForm=class(Forms.TForm)
вместо TMyForm=class(TForm)
, просто чтобы добавить эту переменную на все формы):
TMyForm=class(TForm)
...
public
PleaseFreeAndNilMe:=Boolean;
...
procedure TMyForm.FormCreate(Sender: TObject);
begin
PleaseFreeAndNilMe:=False;
...
end;
procedure TMyForm.FormClose(Sender: TObject; var Action: TCloseAction);
begin
...
PleaseFreeAndNilMe:=True;
end;
Если вы предпочитаете взломанную версию:
TForm=class(Froms.TForm)
public
PleaseFreeAndNilMe:=Boolean;
end;
procedure TForm.FormCreate(Sender:TObject);
begin
inherited Create(Sender);
PleaseFreeAndNilMe:=False;
end;
procedure TForm.FormClose(Sender:TObject;var Action:TCloseAction);
begin
PleaseFreeAndNilMe:=True;
inherited FormClose(Sender,Action);
end;
Но, как я уже сказал, перед доступом к любому члену (или просто к тому, где вы выполняете сравнение с нулем) просто вызовите глобальную функцию, передающую ссылку (независимо от того, был ли он нулем или нет), закодированный как:
function IsNilTheForm(var TheForm: TMyForm);
begin
if nil=TheForm
then begin // The form was freed and nil
IsNilTheForm:=True; // Return value
end
else begin // The form refence is not nil, but we do not know is it has been freed or not
try
if TheForm.PleaseFreeAndNilMe
then begin // The form is not freed but wants to
try
TheForm.Free;
except
end;
try
TheForm:=Nil;
except
end;
IsNilTheForm:=True; // Return value
end
else begin // The form is not nil, not freed and do not want to be freed
IsNilTheForm:=False; // Return value
end;
except // The form was freed but not set to nil
TheForm:=Nil; // Set it to nil since it had beed freed
IsNilTheForm:=True; // Return value
end;
end;
end;
Итак, где вы делаете if nil=MyForm then ...
, теперь вы можете сделать if (IsNilTheForm(MyForm)) then ...
.
Вот и все.
Лучше решение по таймеру, поскольку форма освобождается как можно скорее (используется меньше оперативной памяти), с помощью трюка PleaseFreeAndNilMe
форма не освобождается до вызова IsNilTheForm
(если вы не освободите его где-либо еще).
То, что IsNilTheForm
настолько сложно, потому что он учитывает все состояния (для материнской платы с несколькими сокетами и многопоточных приложений) и позволяет коду освободить / удалить его где-либо еще.
Конечно, эта функция должна вызываться в основном потоке и в атомарном исключении.
Освободить форму и обнулить ее указатель - нетривиальная вещь, в большинстве случаев, когда пользователь может закрыть ее в любое время (поскольку не запускается код вне формы).
Большая проблема заключается в следующем: когда пользователь закрывает форму, у него нет способа получить обработчик события, который запускается из этой формы, и после того, как форма завершает все свои действия.
Вообразите теперь, когда кодер поставил много Application.ProcessMessages; в любом месте приложения, а также в этой форме и т. д. ... и не позаботился об условиях гонки ... попробуйте освободить и обнулить такую форму после того, как пользователь попросит ее закрыть ... это кошмар , но может быть решена с помощью взломанной версии TForm, в которой есть переменная, которая сообщает, что форма не была освобождена, но хочет ее.
Теперь представьте, что вы используете взломанную TForm и хотите нормальную TForm, просто определите ее как ...= class(Forms.TForm)
, так что теперь она будет иметь эту дополнительную переменную. Поэтому вызов IsNilTheForm
будет действовать как сравнение с nil.
Надеюсь, что это помогает VCL-кодерам исправлять такие вещи, как, например, вызывать событие, когда объект уничтожается, освобождается, очищается, скрывается и т. Д., Из кода этого объекта, как в основной форме и т. Д. сделать жизнь проще ... или просто исправить это ... Close and Free подразумевает установку на Nil всех ссылок, которые указывают на это.
Есть еще одна вещь, которую можно сделать (но я всегда стараюсь ее избегать): иметь несколько переменных, которые указывают на одну и ту же форму (не на копии), которая подвержена множеству ошибок, вы свободны один и нужно обнулить их все и т. д. Код, который я показываю, также совместим с этим.
Я знаю, что код закончен ... но Free и Nil форма сложнее, чем мой код.