我最近遇到(再次)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 对将被省略。
因此虽然它可能是编译器可以利用的优化,但它必须正确地执行以便不违反规则。
答案 0 :(得分:16)
如果声明您的方法将接口变量作为const传递,例如:
,则会发生这种情况procedure Frob(const Grob: IGrobber);
那不太对劲。为了存在泄漏,您需要在代码中没有任何内容可以引用新创建的对象。所以,如果你写:
Frob(grob);
没有问题,因为界面grob
已经至少有一个参考。
当你写下来时会出现问题:
Frob(TGrobberImplementer.Create);
在那种情况下,没有任何东西需要引用接口,所以它被泄露了。好吧,只要Frob
的实现中没有任何内容引用它,它就会被泄露。
我做了一件坏事吗?
嗯,这取决于。我不认为你所做的事情会有什么特别糟糕的。在性能方面存在缺点,因为接受接口参数的所有函数现在都必须使用隐式try / finally块添加和释放引用。只有你能判断这是否重要。
更重要的问题与您无法控制的代码有关。你给
procedure OnDocumentComplete(Sender: TObject; const pDisp: IDispatch; var URL: OleVariant);
作为一个例子。那里没有问题,因为你从来没有打过那种方法。它是您实现的事件处理程序。框架调用它,并传递已经引用的接口。
真正的问题来自RTL中声明的方法或您调用的任何其他第三方代码。如果您正在调用这些方法,并且他们使用const
接口参数,那么您可能会陷入陷阱。
虽然很烦人,但它很容易解决。
grob := TGrobberImplementer.Create;
Frob(grob);
我处理这个问题的理由是这样的:
const
接口参数。const
。