Delphi接口引用计数的实现是否具有未来性?

8

我有一个助手类,在整个应用程序中将被广泛使用。实现依赖于接口引用计数,大致的想法是:

...
var
  lHelper: IMyHelper;
begin
  lHelper := TMyHelper.Create(some params);
  ...some code that doesn't have to access lHelper
end;

因此,这个实现依赖于IMyHelper在方法结束时超出作用域,但不是在此之前。

那么我的问题是,我能否确信某个未来的Delphi编译器不会聪明地释放接口,在方法的其余部分中未访问变量后立即释放接口?


1
这篇博客文章的评论可能对您有所帮助。链接 - Uli Gerhardt
担心未使用的实例有什么意义呢? - OnTheFly
我在这里问了一个类似的问题:https://dev59.com/tGsz5IYBdhLWcg3wpZdF - David Heffernan
1
@mjn 这就是我们在日志类中使用的性能分析功能:在方法开头写入 TSynLog.Enter; 将自动对其进行性能分析,并在函数 TSynLog.Enter 返回的接口生成“离开”日志条目。 - Arnaud Bouchez
我也在使用这种方法来进行本地垃圾收集、链式方法上的垃圾收集和分析器。 - user315561
显示剩余3条评论
2个回答

10
我认为你可以对此感到自信。这种超出范围的模式可能会在这个方法的指令块中保持全局性。这将是一种破坏性的变化。
请参见Embarcadero的巴里·凯利(Barry Kelly)在此评论中所说:
“至于你之前的评论,关于显式变量:在假设的(并且具有破坏性变化的)情况下,当我们优化接口变量使用时,我们很可能不仅会破坏所描述的类RAII功能,而且还会破坏显式变量方法;分配给FooNotifier和BarNotifier的值并没有被使用,所以“理论上”它们可以更早地被释放,并且潜在地甚至可以重用相同的存储空间。”
“但当然,接口的销毁可能会产生副作用,这正是本文所依赖的效果。改变语言,使得这些副作用具有可见的变化,不是我们愿意做的事情。”
因此,你可以猜测Embarcadero不会在这里引入任何向后兼容性的变化。重用接口内存的好处并不值得破坏兼容性和引入副作用:在当今这个时代,保存一个指针(4或8字节)已经不值得了,特别是当栈已经分配和对齐时(x64模型使用的堆栈比x86更多)。
只有在引入语言垃圾收集器的情况下(从我个人的角度来看,我不想要),对象的生命周期可能会发生变化。但在这种情况下,生命周期可能会更长。
在所有情况下,你可以创建自己的代码,以确保它将在方法结束时被释放:
var
  lHelper: IMyHelper;
begin
  lHelper := TMyHelper.Create(some params);
  try 
    ...some code that doesn't have to access lHelper
  finally
    lHelper := nil; // release the interface count by yourself
  end;
end;

实际上,这是编译器已经生成的代码。写这个代码将完全多余,但它将确保编译器不会欺骗您。
在讨论接口和引用计数时,请考虑Delphi中循环引用的潜在问题。请参见关于接口循环引用需要“弱指针”的这篇优秀文章(即“示例2-15”)
其他语言(如Java或C#)使用垃圾回收器来解决此问题。Objective C使用显式的“零化弱指针”机制来解决它 - 请参见这个讨论这个SO答案可能的实现方式。也许未来版本的Delphi可以考虑使用类似于Objective C中引入的ARC模型的实现方式。但我怀疑会有一个明确的语法来保持与现有代码的兼容性。

这个想法是避免编写 try..finally,实际上在这种情况下我认为并不需要它,因为在结尾处设置 lHelper := nil 即可,如果出现异常,则会退出作用域并释放接口。话虽如此,我也不愿意编写 lHelper := nil。 - user219760
@DanielMaurić 你不需要这样做。当前的语言指南规定它将在作用域结束时被销毁。没有人能保证任何事情的未来。他们甚至可以将begin/end更改为start/finish,但这不太可能吧。 - David Heffernan
我们可以确定的是,如果这样的改变真的发生了,将会阻止许多人升级他们的代码库,并且会导致产品销售量下降,因此这种情况不会发生。 - Warren P
@Warren:有些人会把Unicode开关归为同一类别。<gd&r> - Uli Gerhardt
这是一个人们可能认为是个好主意的开关示例,直到他们经历了现实,发现它会造成无法恢复的混乱,并且会完全毁掉 Delphi。工程师不应该在客户表现出愚蠢时听从他们的意见,这证明 RAD Studio 产品中的工程团队没有被一群穿着西装的 MBA 傻瓜所否决,这些傻瓜相信“顾客永远是对的”。 - Warren P
我已经添加了一个链接到http://stackoverflow.com/questions/487311/how-to-implement-reference-counted-objects-in-delphi#487387。在我看来,这是使用Delphi接口实现弱引用的最完整方法。在任何情况下都不是易事! - Arnaud Bouchez

4
文档中说道(重点标注为我):

在Win32平台上,接口引用通常通过引用计数进行管理,这依赖于从System/IInterface继承的_AddRef和_Release方法。使用默认的引用计数实现,当一个对象仅通过接口引用时,无需手动销毁它;当最后一次引用该对象超出范围后,该对象会自动销毁

局部变量的作用域是方法,因此当前规范是在方法完成前不会调用_Release
从未保证规格将来不会更改,但我认为对于语言的这个部分进行更改的可能性微乎其微。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接