如何在Delphi控件中实现接口

4

我了解到,如果一个类实现了一个接口,那么它现在是引用计数的,并且不应该通过调用free来管理其内存。

然而,如果您创建一个自定义控件并让它实现一个接口,那么如何防止其owner管理其内存呢?例如,当您在设计时将其放在表单上时,引用计数和所有者内存管理是否会相互冲突?

感谢您的时间。

1个回答

6

控件不受此行为的影响,因为它们不继承自TInterfacedObject。因此,它们不进行引用计数,它们的引用计数被设计为固定为-1*)

所有控件都继承自TComponent,其代码如下:

TComponent = class(TPersistent, IInterface, IInterfaceComponentReference)  

TComponent中的引用计数如下所示:
function TComponent._AddRef: Integer;
begin
  if FVCLComObject = nil then Result := -1
       // -1 indicates no reference counting is taking place
  else Result := IVCLComObject(FVCLComObject)._AddRef;
end;

function TComponent._Release: Integer;
begin
  if FVCLComObject = nil then Result := -1   
  // -1 indicates no reference counting is taking place
  else Result := IVCLComObject(FVCLComObject)._Release;
end;

*)需要注意的是,如果VCLComObject已分配,则它们会遵循该对象的引用计数(通常不会停留在-1)。
大多数组件的VCLComObject为nil。
它仅用于IDE生成的组件包装COM对象。
请参见:TComponent.ComObject

因此,您可以随心所欲地添加接口。只要记得在完成后释放组件,就不会出现问题。

您可以通过测试以下内容来查看控件是否进行引用计数:

DoesNotRefCount:= Supports(MyObject, IInterfaceComponentReference) 
                  and (TComponent(MyObject).VCLComObject = nil);  

不要调用您的对象上的 _AddRef 来测试它是否返回 -1,因为那可能会破坏使用引用计数的对象。如果您的引用计数对象从 0 开始,并且您执行了 _AddRef,然后又执行了 _Release,那么您将销毁该对象,即使 Delphi 正要在两条指令后调用 _AddRef。
如果您想创建自己不进行引用计数的对象,最好添加一个标记接口。
INoRefCounting = interface
 ['{CAD60ADF-C49A-46FB-BB5A-CC54BD22C7EB}']
end;

在较新的Delphi版本中,您可以从TSingletonImplementation继承,该类为您执行虚拟引用计数。

在旧版Delphi中,您需要从TObject(或TWhatever)继承,并按以下方式实现虚拟引用计数:

function TMyObject.QueryInterface(const IID: TGUID; out Obj): HResult; {stdcall;}
begin
  if GetInterface(IID, Obj) then Result := S_OK
  else Result := E_NOINTERFACE;
end;

function TMyObject._AddRef: Integer; {stdcall;}
begin
  Result := -1;
end;

function TMyObject._Release: Integer; {stdcall;}
begin
  Result := -1;
end;  

最后说明
如果您想确保自定义控件不进行引用计数(reference counting),则需要覆盖 _AddRef/_Release 方法,以去除基于 VCLComObject 的条件引用计数。

警告
如果您的控件寿命很短且仍在更长时间地持有对这些控件接口的引用,那么您将遇到问题。
如果您遇到此问题,可以在 _AddRef_ReleaseDestroy 方法中添加调试代码,跟踪引用计数并在引用计数太晚或太早达到零时发出信号。


1
FVCLComObject對於大多數組件通常為nil。 它僅用於IDE生成的組件包裝COM對象(參見TComponent.GetComObject(),該函數賦值於FVCLComObject,雖然TComponent.VCLComObject是公共的,因此您也可以手動賦值一個COM對象)。 - Remy Lebeau
虽然可以使接口对象“不进行引用计数”(即不维护该计数),但对 AddRefRelease 的调用仍将继续发生。更严重的是,您不能添加接口并开始释放对象,并确保没有问题。如果您明确地Free一个当前具有接口引用的对象,则在这些引用被Release时,您遇到严重的问题。最好保持引用计数,并在对象以计数> 0(您不应这样做)的方式优雅地报告对象是否已被Free - Deltics

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