Должен ли я поставить блок try-finally после каждого Object.Create? - PullRequest
7 голосов
/ 27 мая 2010

У меня есть общий вопрос о лучшей практике в OO Delphi. В настоящее время я помещаю блоки try-finally везде, где создаю объект, чтобы освободить этот объект после использования (чтобы избежать утечек памяти). E.g.:

aObject := TObject.Create;
try
  aOBject.AProcedure();
  ...
finally
  aObject.Free;
end;

вместо:

aObject := TObject.Create;
aObject.AProcedure();
..
aObject.Free;

Как вы думаете, это хорошая практика или слишком много накладных расходов? А как насчет производительности?

Ответы [ 8 ]

17 голосов
/ 27 мая 2010

Лучше всего использовать try-finally.

В случае возникновения исключения этот объект будет освобожден.

Что касается производительности: измерьте перед оптимизацией.

15 голосов
/ 28 мая 2010

По моему мнению, есть только одна причина, по которой строительство объекта не должно сопровождаться ( или "in", как указал Мейсон ) a try / finally блок.

  1. Если временем жизни объекта управляет другой объект.

Это управление может принимать три формы:

  1. Ссылка на объект имеет область действия за пределами локального блока и освобождается в другом месте - как член поля, освобожденный в деструкторе.
  2. Объект немедленно добавлен в список, который отвечает за освобождение объекта позже.
  3. Объект со связанным менеджером времени жизни, например способ передачи владельца элементу управления VCL при создании.

С # 1 , , когда ссылка имеет более широкую область действия , ссылка должна быть немедленно установлена ​​равной нулю, если она не создана сразу. Таким образом, когда он проверяется для справки, вы знаете, что у вас есть точные данные. Это наиболее часто встречается в объектах-членах, которые создаются как часть более крупного класса, а затем очищаются при уничтожении родительского объекта.

С # 2 , , когда объект добавляется в список , вы хотите использовать блок try-except (один из немногих, когда я его использую) на всякий случай исключение происходит после создания объекта и до его добавления в список управления. В идеале первая строка после конструкции добавляет его в список, или список на самом деле является фабричным классом, который дает вам объект, уже добавленный в список.

С # 3 , , когда у объекта есть другой менеджер времени жизни , вы действительно должны убедиться, что управление этим менеджером является правильным решением. Если вы создаете элемент управления VCL, у вас может возникнуть искушение владеть формой (или любым другим элементом управления), но это фактически накладывает дополнительные затраты на конструкцию и разрушение. Если возможно, вы должны явно освободить его, это особенно верно, если вы включаете элемент управления один раз, тогда вы знаете, что освободите его в деструкторе вашей формы или когда он закроется. Единственный раз, когда вы не можете сделать это, это если создание элемента управления является более динамичным.

Так что да, лучше всего использовать множество try / finally блоков. У вас должно быть только несколько try / except блоков, и большинство из них должны перехватывать очень специфические типы исключений и / или повторно вызывать исключение. Если у вас больше try / except блоков try / finally, то вы делаете неправильно .

13 голосов
/ 27 мая 2010

Как сказал Фрэнк, «что касается производительности: измеряйте, прежде чем оптимизировать». Повторяя это, чтобы подчеркнуть.

Кроме того, если вы создаете группу объектов в методе, вам не нужно использовать блок try..finally для каждого из них. Это может привести к уродливой путанице. create, try, create, try, create, try, do something, finally, free, finally, free, finally, free. Тьфу! Вместо этого вы можете установить ссылки на объекты на nil в верхней части метода, затем создать их все, сделать один блок try и освободить их все в разделе finally.

Это сэкономит некоторые накладные расходы и производительность (хотя вы, вероятно, никогда не заметите разницу), но, что более важно, это сделает ваш код чище и проще для чтения при сохранении того же уровня безопасности.

5 голосов
/ 27 мая 2010

В прошлом году на днях в Delphi Developer я видел частный тайник кода Марко Канту, и он звонил, наконец, каждый раз, когда создает что-то.

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

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

Очевидно, что иногда это немного навязчиво, и если в вашей рабочей среде нет места для того, чтобы быть точным в вашей робости, это может заставить вас выглядеть как лакомый кусочек. Но я думаю, что это хорошая идея. Это похоже на попытку Delphi по принудительному сбору мусора вручную.

5 голосов
/ 27 мая 2010

Чтобы ответить на вторую часть вашего вопроса:
try finally вряд ли имеет какие-либо накладные расходы.

На самом деле существует множество методов, имеющих неявный блок try ... finally. Например, просто наличие функции, использующей локальную переменную любого типа интерфейса и присвоение значения.

- Йерун

2 голосов
/ 27 мая 2010

Если вы создаете объект в конструкторе класса, и объект будет принадлежать включающему классу, вы захотите освободить его в деструкторе класса-владельца.

Я склонен использовать FreeAndNil () вместо вызова Free.

РЕДАКТИРОВАТЬ: Но, как говорили другие, вы определенно всегда хотите освободить то, что вы создаете.

1 голос
/ 28 мая 2010

Да, это всегда хорошая идея (существенная), если код, который создает объект, отвечает за его освобождение. Если нет, то попытка / окончательно не подходит, но опять же, ни одна из них. Бесплатно в любом случае!

Однако может быть обременительно иметь этот шаблонный код, пронизывающий вашу «бизнес-логику», и вы можете рассмотреть подход, который имеет такую ​​же гарантию освобождения ваших объектов, но гораздо чище (и имеет другие преимущества), , например, моя собственная реализация AutoFree () .

С помощью функции AutoFree () ваш код может быть написан:

aObject := TObject.Create;
AutoFree(@aObject);

aObject.AProcedure();

или в качестве альтернативы, поскольку в реализации используется ссылка на ссылку (для включения автоматического NIL'а, а также Free'ing), вы даже можете предварительно зарегистрировать свои ссылки, которые вы хотите, чтобы AutoFree'd сохранял такие декларации обслуживания вне вашей бизнес-логики и держите реальное «мясо» вашего кода настолько чистым, насколько это возможно (это особенно полезно, когда потенциально многократные объекты должны быть Free'd):

AutoFree(@aObject1);
AutoFree(@aObject2);

aObject1 := TObject.Create;
aObject1.AProcedure();

// Later on aObject2 is (or may be) also created
 :

В моем исходном сообщении не показано более позднее дополнение к механизму поддержки регистрации нескольких ссылок в одном вызове AutoFree (), но я уверен, что вы сможете самостоятельно определить изменения, необходимые для поддержки этого, если вы хотите иметь возможность сделать это:

AutoFree([@aObject1, @aObject2]);

aObject1 := TObject.Create;
aObject1.AProcedure();

// Later on aObject2 is (or may be) also created
 :
0 голосов
/ 28 мая 2010

Даже если очень рекомендуется , я не всегда буду это делать.

Мои правила использования или не использования try / finally:

  • Вероятность того, что этот объект упадет и сгорит.
  • Количество раз, когда объект создан. Если я знаю, что ваш объект создается редко (не более 5-6 раз за время жизни приложения), я могу жить с утечкой памяти в 20 КБ - в случае, если он «умирает», но не освобождается.
  • Количество утечки памяти в случае сбоя объекта.
  • Сложность кода. Попробуйте / кроме того, чтобы код выглядел действительно ужасно. Если есть процедура из 5 строк, я всегда использую try / исключением.
  • Длина файла приложения. Если ваше приложение должно работать в течение нескольких дней, то я определенно хочу освободить ЛЮБОЙ фрагмент памяти, иначе утечка будет накапливаться.

Единственное место, где трудно принять решение, это когда вам нужна производительность при создании объекта тысячи раз (например, в цикле). В этом случае я не использую try /, за исключением случаев, когда объект выполняет простые задачи, и есть небольшая вероятность увидеть его сбой.

...