我应该在每个Object.Create之后放一个try-finally块吗?

时间:2010-05-27 16:35:54

标签: delphi try-finally

我对OO Delphi的最佳实践有一个普遍的疑问。目前,我将try-finally块放在我创建对象的任何地方,以便在使用后释放该对象(以避免内存泄漏)。 E.g:

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

而不是:

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

你认为这是好的做法还是过多的开销?那表现怎么样?

8 个答案:

答案 0 :(得分:17)

使用try-finally绝对是最好的做法。

如果引发异常,将释放该对象

至于性能:优化之前度量

答案 1 :(得分:15)

在我看来,只有一个原因是对象构造 不应该 后跟(or "in" as Mason pointed outtry / finally块。

  1. 如果对象的生命周期由另一个对象管理。
  2. 这种管理可以采取三种形式:

    1. 对象的引用具有超出本地块的范围,并在其他地方释放 - 就像在析构函数中释放的字段成员一样。
    2. 一个对象立即添加到稍后负责释放该对象的列表中。
    3. 具有关联生命周期管理器的对象,例如如何将所有者传递给构造中的VCL控件。
    4. #1 时,当引用范围更广时,如果不立即构建引用,则应立即将引用设置为nil。这样,当检查参考时,您知道您有准确的读数。这对于构造为较大类的一部分的成员对象最常见,然后在销毁父对象时进行清理。

      使用#2 时,将对象添加到列表时,您希望使用try-except块(我使用过的几次之一) )以防万一在构造对象之后和添加到管理列表之前发生异常。理想情况下,构造后的第一行是将其添加到列表中,或者列表实际上是一个工厂类,它为您提供已添加到列表中的对象。

      #3 当一个对象有另一个生命周期管理器时,你真的应该确保让该管理员管理它是正确的事情。如果您正在构建一个VCL控件,您可能想要让表单(或任何其他控件)拥有它,但这实际上会给构造和破坏带来额外的开销。如果可能的话,你应该明确地释放它,如果你将控件置于一次,那么你就知道你将在你的表单的析构函数中或者它关闭时释放它。唯一不能做的就是控件创建更具动态性。

      所以是的,使用大量try / finally块是最佳做法。您应该只有几个try / except块,并且大多数块都应该捕获非常特定的异常类型,和/或重新引发异常。如果您有try / excepttry / finally个阻止,那么 您做错了

答案 2 :(得分:13)

正如弗兰克所说,“至于绩效:优化前的衡量标准。”重复它来强调。

此外,如果您在方法中创建了一堆对象,则不需要为每个对象使用try..finally块。这可能会导致丑陋的缩进。 create, try, create, try, create, try, do something, finally, free, finally, free, finally, free.唉!相反,您可以在方法的顶部设置对象引用 nil ,然后创建它们,执行一个尝试块,并在finally部分中释放它们。

这将节省一些开销和性能,(虽然你可能永远不会注意到差异,但更重要的是它会使你的代码更清晰,更容易阅读,同时保持相同的安全水平。

答案 3 :(得分:5)

回答你问题的第二部分:
try finally几乎没有任何开销。

实际上有很多方法都有一个隐含的尝试... finally块。例如,只使用一个函数使用任何接口类型的局部变量并指定一个值具有。

- 的Jeroen

答案 4 :(得分:5)

去年在Delphi Developer的几天里,我看到了Marco Cantu的一些私密代码,他总是在每次创建任何东西时都会尝试。

有人问过他,他说他一直试着这样做。

但是当涉及到进入和退出关键部分时,对于多线程代码来说尤其是一个好主意,虽然这不是关于主题的,但要记住这是一件好事。

显然,有时它会有点突兀,如果你的工作环境没有在你的工作环境中被点缀,那么它可能会让你看起来像一双礼服。但我认为这是一个好主意。这有点像Delphi尝试强制手动垃圾收集。

答案 5 :(得分:2)

如果你在类的构造函数中创建对象,并且该对象将由封闭类拥有,那么你将需要在拥有类的析构函数中释放它。

我倾向于使用FreeAndNil()而不是调用Free。

编辑:但正如其他人所说,你一定想要释放你创造的东西。

答案 6 :(得分:1)

是的,如果创建对象的代码负责释放它,那么它总是一个好主意(必不可少)。如果没有,那么try / finally是不合适的,但是再也不是。在任何情况下都是.Free!

然而,使用这个样板代码来处理你的“业务逻辑”可能很麻烦,你可能想要考虑一种方法,它具有相同的保证释放你的对象但更清洁(并具有其他好处)的方法, such as my own AutoFree() implementation

使用AutoFree(),您的代码可以编写:

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

aObject.AProcedure();

或者,由于实现使用对引用的引用(以启用自动NIL和自由),您甚至可以预先注册您希望自动进行的引用以保持这样的引用家政服务声明远离您的业务逻辑,并尽可能保持代码的真正“干净”(当需要免费提供多个对象时,这尤其有用):

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
 :

答案 7 :(得分:-4)

即使非常推荐这样做,我也不会一直这样做。

我使用或不使用try / finally的规则:

  • 该对象崩溃和燃烧的可能性。
  • 创建对象的次数。如果我知道您的对象很少被创建(在应用程序生命周期内不超过5-6次),我可以忍受20KB内存泄漏 - 如果它'死'而没有被释放。
  • 对象崩溃时泄漏的内存量。
  • 代码复杂性。尝试/除了使代码看起来非常难看。如果有5行程序,我总是使用try / except。
  • 应用程序文件范围。如果您的应用程序需要运行数天,那么我明确地想要释放任何内存,否则泄漏将会累积。

唯一难以做出决定的地方是在创建对象数千次时需要性能时(例如循环)。在这种情况下,我不使用try / except如果对象执行简单任务并且很少有机会看到它崩溃。