У меня есть общий вопрос о лучшей практике в Delphi OO. В настоящее время я поместил попытку наконец блоки где угодно, я создаю объект освободить тот объект после использования (для предотвращения утечек памяти). Например:
aObject := TObject.Create;
try
aOBject.AProcedure();
...
finally
aObject.Free;
end;
вместо:
aObject := TObject.Create;
aObject.AProcedure();
..
aObject.Free;
Вы думаете, что это - хорошая практика или слишком много служебное? И что относительно производительности?
На мой взгляд, есть только одна причина, по которой за конструкцией объекта не следует следовать ( или "in", как указал Мейсон ) a try / finally
блок.
Это управление может принимать три формы:
С # 1 , , когда ссылка имеет более широкую область действия , ссылка должна быть установлена на ноль немедленно, если она не создается сразу. Таким образом, когда вы проверяете его для справки, вы будете знать, что у вас есть точные показания. Это чаще всего встречается с объектами-членами, которые создаются как часть более крупного класса, а затем очищаются при уничтожении родительского объекта.
С # 2 , , когда объект добавляется в список , вы хотите использовать блок try-except
(один из немногих случаев, когда я используйте один) на тот случай, если возникнет исключение после создания объекта и до того, как он будет добавлен в список управления.В идеале первая строка после построения добавляет его в список, или список на самом деле является фабричным классом, который дает вам объект, уже добавленный в список.
С # 3 , , когда у объекта есть другой менеджер времени жизни , вы действительно должны убедиться, что управление им от этого менеджера является правильным. Если вы создаете элемент управления VCL, у вас может возникнуть соблазн передать его форме (или любому другому элементу управления), но на самом деле это создает дополнительные накладные расходы на создание и разрушение. Если возможно, вы должны явно освободить его, это особенно верно, если вы поместите элемент управления один раз, тогда вы знаете, что освободите его в деструкторе вашей формы или когда он закроется. Вы не можете этого сделать только в том случае, если создание элемента управления более динамично.
Так что да, рекомендуется использовать много блоков try / finally
. У вас должно быть только несколько блоков try / except
, и большинство из них должны перехватывать очень определенные типы исключений и / или повторно вызывать исключение. Если у вас больше try / except
, чем try / finally
блоков, то вы делаете это неправильно .
Определенно лучше всего использовать try-finally.
В случае возникновения исключения этот объект будет освобожден.
Что касается производительности: измерьте перед оптимизацией.
Да, это всегда хорошая идея (необходимая), если код, создающий объект, отвечает за его освобождение. Если нет, то try / finally не подходит, но, опять же, тоже .Free в любом случае!
Однако может быть обременительным иметь этот шаблонный код для дополнения вашей «бизнес-логики», и вы можете рассмотреть подход, который дает такую же гарантию освобождения ваших объектов, но который намного чище (и имеет другие преимущества), , например, моя собственная реализация AutoFree () .
С AutoFree () ваш код может быть записан:
aObject := TObject.Create;
AutoFree(@aObject);
aObject.AProcedure();
или, альтернативно, поскольку реализация использует ссылку на ссылку (для включения автоматического NIL'ing, а также Free'ing), вы можете даже предварительно зарегистрируйте свои ссылки, которые вы хотите использовать в AutoFree'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
:
Как сказал Фрэнк, «что касается производительности: измерьте, прежде чем оптимизировать». Повторяя это, чтобы подчеркнуть.
Кроме того, если вы создаете группу объектов в методе, вам не нужно использовать блок try..finally для каждого из них. Это может привести к уродливому беспорядку с отступами. создавать, пробовать, создавать, пробовать, создавать, пробовать, делать что-то, наконец, бесплатно, наконец, бесплатно, наконец, бесплатно.
Ух! Вместо этого вы можете установить ссылки на объекты на nil в верхней части метода, затем создать их все, выполнить один блок try и освободить их все в разделе finally.
Это снизит накладные расходы и производительность (хотя вы, вероятно, никогда не заметите разницы), но, что более важно, сделает ваш код более чистым и легким для чтения при сохранении того же уровня безопасности.
Чтобы ответить на вторую часть вашего вопроса:
попробуйте, наконец,
почти не имеет накладных расходов.
На самом деле существует множество методов с неявным блоком try ... finally. Например, просто имея функцию, использующую локальную переменную любого типа интерфейса и присваивая ей значение.
- Джерун
Если вы создаете объект в конструкторе класса и объект будет принадлежать вложенному классу, вы захотите освободить его в деструкторе класса-владельца.
Я обычно использую FreeAndNil() вместо вызова Free.
EDIT: Но, как сказали другие, вы определенно всегда хотите освободить то, что вы создаете.
Даже если очень рекомендуется делать это, я не всегда буду это делать.
Мои правила использования или неиспользования try/finally:
Единственное место, где трудно принять решение - это когда вам нужна производительность при создании объекта тысячи раз (например, в цикле). В этом случае я не использую try/except, если объект выполняет простые задачи и есть небольшой шанс увидеть его крах.
В прошлом году на Delphi Developer days я видел некоторые частные запасы кода Марко Канту, и он вызывает try finally каждый раз, когда что-либо создает.
Кто-то спросил его об этом, и он сказал, что пытается делать это все время.
Но это особенно хорошая идея для многопоточного кода, когда дело доходит до входа и выхода из критических разделов, хотя это не по теме, это полезно помнить.
Очевидно, что иногда это немного навязчиво, и если в вашей рабочей среде не принято быть точным в своей робости, это может сделать вас похожим на пустышку. Но я думаю, это хорошая идея. Это похоже на попытку Delphi принудительно выполнить сборку мусора вручную.