如何在 delphi 控件中实现接口
How to implement an interface in a delphi control
我读到如果 class 实现了一个接口,它现在被引用计数并且不应该通过调用 free
来管理它的内存。
但是,如果您制作一个自定义控件并让它实现一个接口,您如何防止其 owner
管理其内存?例如,当您在设计时将其放在表单上时,引用计数和所有者内存管理会发生冲突吗?
感谢您的宝贵时间。
控件不受此行为影响,因为它们不继承自 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
来测试它是否 returns -1,因为这可能会破坏使用引用计数的对象。
如果您的 ref 计数对象从 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
方法中,您可以在这些方法中跟踪引用计数并在引用计数达到零时发出信号迟到或太早。
我读到如果 class 实现了一个接口,它现在被引用计数并且不应该通过调用 free
来管理它的内存。
但是,如果您制作一个自定义控件并让它实现一个接口,您如何防止其 owner
管理其内存?例如,当您在设计时将其放在表单上时,引用计数和所有者内存管理会发生冲突吗?
感谢您的宝贵时间。
控件不受此行为影响,因为它们不继承自 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
来测试它是否 returns -1,因为这可能会破坏使用引用计数的对象。
如果您的 ref 计数对象从 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
方法中,您可以在这些方法中跟踪引用计数并在引用计数达到零时发出信号迟到或太早。