我已经读过,如果一个类实现了一个接口,它现在被引用计数,不应该通过调用free
来管理它的内存。
但是,如果您创建自定义控件并使其实现接口,那么如何阻止其owner
管理其内存?例如,当您在设计时将其放在表单上时,引用计数和所有者内存管理会不一致?
感谢您的时间。
答案 0 :(得分: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
对于大多数组件都是零
它仅用于IDE生成的组件包装器来包装COM对象
See: 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;
最终说明
如果您想100%确定自定义控件不进行引用计数,则必须覆盖_AddRef
/ _Release
方法以根据VCLComObject
删除条件引用计数。
警告强>
如果控件的生命周期很短,并且您仍然保持对这些控件的接口的引用时间更长,那么您将遇到问题。
如果您遇到这种情况,可能需要将调试代码添加到_AddRef
,_Release
和Destroy
方法中,在这些方法中,您可以跟踪引用计数并发信号通知引用计数是否达到零迟到或太早。