В моей практике довольно часто встречается ситуация, когда на форме/фрейме (либо в каком-либо объекте), создаётся некоторое количество вспомогательных объектов (в Run-Time). Если эти объекты являются наследниками от TComponent, то в конструктор Create такого объекта можно передать компонет-владелец AOwner; владелец отвечает за автоматическое удаление объекта. Однако если вспомогательные объекты не являются компонентами, либо если нельзя явно указать компонент-владелец, то в таких случаях вспомогательные объекты приходится уничтожать вручную явно. Далее я буду говорить именно о таких случаях.
Понятно, что если Вы забыли уничтожить объект, то это приведёт к утечке памяти. А чтобы не забыть уничтожить объект, можно себя обязать таким правилом:
если я пишу SomeObject := TSomeObject.Create; то сразу же делаю следующее:
- создаю закладку на этой строке кода (Ctrl+Shift+1);
- перехожу в метод, который отвечает за освобождение ресурсов;
- добавляю туда SomeObject.Free;
- возвращаюсь к коду, где был создан объект (Ctrl+1);
Впринципе, этого достаточно. Однако, когда объектов создаётся много, а код пока ещё черновой, постоянные переключения между методами напрягают и отвлекают от основной задачи.
Для облегчения задачи освобождения вспомогательных объектов, предлагаю следующий подход:
- создаётся объект-список (назовём его FAutoFreeObjects);
- все вспомогательные объекты, после своего создания сразу же добавляются в этот список;
- в методе освобождения ресурсов – просто уничтожаем все объекты из списка FAutoFreeObjects.
А чтобы по списку не "пробегать" вручную, список FAutoFreeObjects должен быть типа TObjectList из модуля Contnrs. При уничтожении FAutoFreeObjects, все объекты этого списка будут уничтожены автоматически в порядке, обратном добавлению объектов.
Продемонстрирую вышесказанное наглядным примером:
interface uses .. Contnrs; TForm1 = class(TForm) private FAutoFreeObjects: TObjectList; FSomeObject1: TSomeObject1; FSomeObject2: TSomeObject2; .. end; implementation procedure TForm1.OnCreate(Sender: TObject); begin FAutoFreeObjects := TObjectList.Create; .. FSomeObject1 := TSomeObject1.Create; FAutoFreeObjects.Add(FSomeObject1); FSomeObject1.SomeProperty := SomeValue; .. FSomeObject2 := TSomeObject2.Create; FAutoFreeObjects.Add(FSomeObject2); FSomeObject2.SomeProperty := SomeValue; .. end; procedure TForm1.OnDestroy(Sender: TObject); begin FreeAndNil(FAutoFreeObjects); end;
Плюсы такого подхода:
- проще взять за правило добавление объектов в FAutoFreeObjects, чем постоянное переключение между методами инициализации и освобождения ресусров
- как следствие: подход облегчает написание кода на начальном (черновом) уровне
- подход облегчает сопровождение кода
Минусы:
- дополнительные расходы памяти (список объектов)
Развивая идею, можно написать такой метод (от имени формы/фреймы):
function TForm1.AutoFree(AObject: TObject): Pointer; begin if not Assigned(FAutoFreeObjects) then FAutoFreeObjects := TObjectList.Create; FAutoFreeObjects.Add(AObject); Result := AObject; end;
И тогда код обработчика OnCreate формы можно упростить до следующего:
procedure TForm1.OnCreate(Sender: TObject); begin FSomeObject1 := AutoFree(TSomeObject1.Create); FSomeObject1.SomeProperty := SomeValue; .. FSomeObject2 := AutoFree(TSomeObject2.Create); FSomeObject2.SomeProperty := SomeValue; .. end;
5 коммент.:
Good idea
"Все уже украдено до нас"
Вот интересный тред по этому поводу на сайте Embarcaderro
https://forums.embarcadero.com/message.jspa?messageID=27076
Во всех способах свои недостатки. У меня вот например нет такого единого центра владельца.
Как вариант, если классы "свои", то можно добавить к ним описание как интерфеса и использовать их именно как интерфейсы, тогда можно подцепить механизм подсчёта ссылок и будет вам счастье.
Моё отношение к повсеместному использованию "умных" указателей и интерфейсов ради единственной цели - автоматического уничтожения объектов -- отрицательное. Каждый механизм нужно использовать для тех целей, для которых он предназначен...
Мой пример показывает лишь один из способов упрощения жизни кодера, когда все объекты формы/фреймы уничтожаются на заранее определённом уровне (в примере - это метод OnDestroy).
P.S.: добавил в заметку метод AutoFree (для ещё большего упрощения написания кода).
Интересная идея, спасибо. Единственный нюанс, который может возникнуть - это порядок уничтожения объектов, но и это в большинстве случаев лечится освобождением объектов в обратном порядке.
> Вот интересный тред по этому поводу на сайте Embarcaderro https://forums.embarcadero.com/message.jspa?messageID=27076
Спасибо, тред действительно интересный.
Отправить комментарий