我了解到,如果一个类实现了一个接口,那么它现在是引用计数的,并且不应该通过调用free
来管理其内存。
然而,如果您创建一个自定义控件并让它实现一个接口,那么如何防止其owner
管理其内存呢?例如,当您在设计时将其放在表单上时,引用计数和所有者内存管理是否会相互冲突?
感谢您的时间。
我了解到,如果一个类实现了一个接口,那么它现在是引用计数的,并且不应该通过调用free
来管理其内存。
然而,如果您创建一个自定义控件并让它实现一个接口,那么如何防止其owner
管理其内存呢?例如,当您在设计时将其放在表单上时,引用计数和所有者内存管理是否会相互冲突?
感谢您的时间。
控件不受此行为的影响,因为它们不继承自TInterfacedObject
。因此,它们不进行引用计数,它们的引用计数被设计为固定为-1*)。
所有控件都继承自TComponent
,其代码如下:
TComponent = class(TPersistent, IInterface, IInterfaceComponentReference)
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);
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
、_Release
和 Destroy
方法中添加调试代码,跟踪引用计数并在引用计数太晚或太早达到零时发出信号。
FVCLComObject
對於大多數組件通常為nil。 它僅用於IDE生成的組件包裝COM對象(參見TComponent.GetComObject()
,該函數賦值於FVCLComObject
,雖然TComponent.VCLComObject
是公共的,因此您也可以手動賦值一個COM對象)。 - Remy Lebeau