【发布时间】:2016-04-20 20:23:42
【问题描述】:
我已经读到,如果一个类实现了一个接口,它现在是引用计数的,并且不应该通过调用 free 来管理它的内存。
但是,如果你制作一个自定义控件并让它实现一个接口,你如何防止它的owner 管理它的内存?例如,当您在设计时将其放在表单上时,引用计数和所有者内存管理会发生冲突吗?
感谢您的宝贵时间。
【问题讨论】:
标签: delphi interface custom-controls
我已经读到,如果一个类实现了一个接口,它现在是引用计数的,并且不应该通过调用 free 来管理它的内存。
但是,如果你制作一个自定义控件并让它实现一个接口,你如何防止它的owner 管理它的内存?例如,当您在设计时将其放在表单上时,引用计数和所有者内存管理会发生冲突吗?
感谢您的宝贵时间。
【问题讨论】:
标签: delphi interface custom-controls
控件不属于这种行为,因为它们不继承自 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,它们会遵循该 Object 的引用计数(通常不会停留在 -1)。VCLComObject 对于大多数组件。
它仅用于 IDE 生成的组件包装器来包装 COM 对象。
See: TComponent.ComObject
因此,您可以为自己的内容添加界面。只要您记得在完成后释放组件,它就可以正常工作。
您可以通过测试来测试控件是否进行引用计数:
DoesNotRefCount:= Supports(MyObject, IInterfaceComponentReference)
and (TComponent(MyObject).VCLComObject = nil);
不要在你的对象上调用_AddRef 来测试它是否返回-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 方法中,您可以在其中跟踪引用计数并在引用计数达到零到迟或过早时发出信号。
【讨论】:
FVCLComObject 通常为零。它仅用于 IDE 生成的组件包装器来包装 COM 对象(请参阅 TComponent.GetComObject(),它分配了 FVCLComObject,尽管 TComponent.VCLComObject 是公共的,因此您可以手动分配 COM 对象)。