Для понимания проблемы, покажу историю развития вопроса. Понятно, что если задачу решать полностью с нуля, то её можно решить разными способами, и можно выбрать решение, когда FreeAndNil будет лучшим и правильным вариантом. Однако в исторической перспективе получилось так, как получилось, я увидел что FreeAndNil всё поломает и решил об этом написать заметку.
Есть у меня один фрейм (TFrame), на который динамически в Run-Time создаётся компонет - превью отчёта. Т.к. превью - это наследник от TComponent, то я использую свойство Owner при создании и не забочусь об явном уничтожении превью. Создание дёргается внутри некоторого метода, который может быть вызван из вне несколько раз, выглядит это примерно так:
procedure TMyPreviewFrame.SomeMethod(SomeParams); begin if FfrxPreviewForm = nil then begin FfrxPreviewForm := TfrxPreviewForm.Create(Self); // далее настройка превью при превом создании end; // далее работа с превью по переданным SomeParams сверху end;Соответственно уничтожение FfrxPreviewForm происходит автоматически при уничтожении фрейма; к экземпляру FfrxPreviewForm за пределами или в деструкторе фрейма я не обращаюсь, поэтому явно обнулять переменную нет необходимости.
В какой-то момент времени, мне понадобилось сохранять текущее состояние превью при выходе из приложения. Сделал я это просто:
type TMyPreviewFrame = class(TFrame) private FfrxPreviewForm: TfrxPreviewForm; procedure SavePreviewOptions; protected procedure Notification(AComponent: TComponent; Operation: TOperation); override; .. procedure TMyPreviewFrame.SavePreviewOptions; begin if FfrxPreviewForm = nil then Exit; .. // сохраняем свойства из FfrxPreviewForm end; procedure TMyPreviewFrame.Notification(AComponent: TComponent; Operation: TOperation); begin if (Operation = opRemove) and (AComponent = FfrxPreviewForm) then begin SavePreviewOptions; FfrxPreviewForm := nil; end; end;
В VCL это работает так: при уничтожении экземпляра фрейма, фрейм (как наследник от TComponent) перебирает все компоненты, которыми владеет, и уничтожает их. При этом компонент оповещает владельца о том, что сейчас будет уничтожен.
Чисто теоретически, SavePreviewOptions может стать публичным методом, поэтому в Notification я добавил очистку ссылки (хотя на текущий момент это не обязательно).
И вот настал момент, когда в методе SomeMethod понадобилась возможность уничтожить FfrxPreviewForm (с сохранением его состояния). Делаю я это примерно так:
procedure TMyPreviewForm.SomeMethod(SomeParams); begin if SomeCondition1(SomeParams) then begin FfrxPreviewForm.Free; Exit; end; if FfrxPreviewForm = nil then begin FfrxPreviewForm := TfrxPreviewForm.Create(Self); // далее настройка превью при превом создании end; // далее работа с превью по переданным SomeParams сверху end;
Надеюсь Вы понимаете, что тут происходит.
А теперь пища для ума: что будет, если FfrxPreviewForm.Free заменить на FreeAndNil(FfrxPreviewForm)?
4 коммент.:
И что будет? Не вызовется деструктор и тем самым не уничтожатся компоненты во фрейме
Ну то есть FreeAndNil помог найти логическую проблему: обращение к компоненту в момент его удаления (читай: в переходном состоянии) ;)
Вы оба не правы. Вспомните о том, что FreeAndNil - он на самом деле работает как NilThenFree.
Т.е. при FreeAndNil(FfrxPreviewForm) сначала произойдёт установка переменной FfrxPreviewForm в nil, затем дёрнется деструктор превью, который дёрнет Notification. И вот в нём условие (AComponent = FfrxPreviewForm) не отработает, а значит и не вызовется SavePreviewOptions.
Т.е. утечек памяти не будет, но логика (когда при уничтожении превью надо сохранять его состояние) поломается. И такое не поддаётся диагностике.
Согласен с GunSmoker, надо изменять логику сохранения
Отправить комментарий