среда, 3 ноября 2010 г.

Автоматическое уничтожение объектов

В моей практике довольно часто встречается ситуация, когда на форме/фрейме (либо в каком-либо объекте), создаётся некоторое количество вспомогательных объектов (в 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

Спасибо, тред действительно интересный.

Отправить комментарий

.

.