【发布时间】:2015-09-10 18:09:24
【问题描述】:
我最近(再次)遇到the Delphi compiler code-gen bug when passing an interface as const 泄露参考。
如果您的方法被声明为将接口变量传递为const,则会发生这种情况,例如:
procedure Frob(const Grob: IGrobber);
解决方法是简单地删除const:
procedure Frob(Grob: IGrobber);
我了解const(以及var 和out)允许您通过引用传递项目。在结构的情况下,这会保存一个参数副本;让您只需将指针传递给项目。
对于Object/Pointer/Interface,不需要通过引用传递,因为它是引用;它已经可以放入寄存器了。
为了不再有这个问题,我进行了讨伐。我搜索了所有源代码树:
const [A-Za-z]+\: I[A-Z]
我删除了大约 150 个实例,其中我将接口作为 const 传递。
但有些是我无法改变的。 TWebBrowser 回调事件声明为:
OnDocumentComplete(Sender: TObject; const pDisp: IDispatch; var URL: OleVariant);
\___/
|
?
我走得太远了吗?我做了坏事吗?
编辑:或者,用一个较少“基于意见” 风格的问题来表述它:是否有任何严重的缺点不 将接口作为 const 传递?
奖励:当 Delphi 不(总是)增加接口引用计数时,它们违反了The Rules of COM:
引用计数规则
规则 1: AddRef 必须为接口指针的每个新副本调用,Release 必须在接口指针的每次销毁时调用,除非后续规则明确允许。
规则 2:一段代码的特殊知识可以允许 AddRef 的两个或多个接口指针的生命周期的开始和结束的关系/Release 对被省略。
因此,虽然它可能是编译器可以利用的优化,但它必须正确执行,以免违反规则。
【问题讨论】:
-
Embarcadero 多年来一直知道这一点。丹尼索普和巴里凯利都告诉我这是一个缺陷。最近,Marco 在 Google+ 上发表了一些狡辩,解释了为什么改变并非易事。我猜他们在编译器中有一个实现选择,如果不进行重大的重新设计,就很难解决这个问题,而且他们真的不知道如何进行重新设计。
-
我相信你知道这一点,但
const带有接口/字符串/dyn_array 的重点不是要避免pass_by_value,而是要避免 ref-counting 和隐藏的 try-finally随之而来。您在问题中的pass_by_value部分详述了问题。 -
@Johan 这很有趣。语义上
const表示函数的实现不能修改参数。引用计数等是在不进行任何修改的情况下进行的优化。如果编译器更有意义,它甚至会通过观察没有进行任何修改来对值参数进行相同的优化。 -
@IanBoyd:您不需要从界面参数中大量删除每个
const。您真正需要做的就是在代码中搜索您在函数调用中内联的接口对象Create()'的任何情况,例如:Func(TMyObject.Create)。这是唯一的泄漏,而不是const本身。 -
@IanBoyd:Delphi 没有违反 COM 规则。仅将接口指针作为输入的函数不需要触摸接口的引用计数。如果每个函数调用都这样做,那将是浪费开销。查看用 C/C++ 编写的 any COM 示例(请记住,COM 主要是为 C/C++ 设计的)。
AddRef()和Release()仅在函数正在修改接口参数时使用,或者接口需要在函数调用之后持续存在。否则,接口参数被视为只是一个常规指针。除非使用const,否则 Delphi 不会这样做。
标签: delphi interface constants