我有一个 Delphi 表单,它提供接口对象背后的功能,代码的其他部分也通过属于表单的属性获取引用。我不能将接口功能委托给子对象,因为表单上的控件/组件提供了太多的功能。我不能使用 TAggregatedObject 或 TContainedObject 将被传递的接口对象的生命周期链接到 Form,因为 TForm 类不继承自 TinterfacedObject 并且 Delphi 不支持多重继承,所以我不能将 TInterfacedObject 混合到继承链中. 如果表单被破坏,而其他一些代码持有表单传递的接口引用之一,这种情况可能会导致访问冲突。谁能想到一个很好的解决这个问题的方法?
2 回答
您可以将接口委托给子对象,只需让该对象包含指向表单的内部指针,以便它可以在需要时访问表单的控件,与您现在已经在做的没有什么不同。
您可以使用TAggregateObject
或TContainedObject
满足您的需要。它们不需要从TInterfacedObject
. 他们只需要一个IInterface
接口指针,并且TComponent
派生自IInterface
(并覆盖_AddRef()
and_Release()
以禁用引用计数),因此您可以将 Form 本身(作为TComponent
后代)作为所需的IInterface
指针传递。
这留下了唯一的问题 - 当其他代码持有活动接口引用时表单关闭。最简单的解决方案是 1) 重写该代码以在 Form 关闭时不保留这些引用,或者 2) 在释放这些引用之前不允许 Form 关闭。
注意:这只有在你的消费者也是从 TComponent 派生的情况下才有效。
为了避免死引用,您可以IInterfaceComponentReference
从表单中查询(在每个 TComponent 上可用),调用GetComponent
该接口并将自己附加到FreeNotification
返回的组件/表单的。
现在发生的事情是:当表单被销毁时,它会通知所有“侦听器”它会通过调用消费者上的方法来销毁自己(Notification
表单)作为操作。从而允许您将您的接口引用归零。但请注意,对象引用和接口引用不能相等。另外,请确保在您不再需要通知时拨打电话,以避免不必要的电话。AComponent
opRemove
RemoveFreeNotification
TSomeConsumer = class(TComponent)
private
FInterfaceToAService: ISomeInterface;
protected
procedure Notification(AComponent: TComponent; Operation: TOperation); override;
public
procedure SetService(const Value: ISomeInterface);
end;
procedure TSomeConsumer.Notification(AComponent: TComponent; Operation: TOperation);
begin
inherited;
if (Operation = opRemove) and (AComponent = TObject(FInterfaceToAService)) then
SetService(nil); // Takes care of niling the interface as well.
end;
procedure TSomeConsumer.SetService(const Value: ISomeInterface);
var
comRef: IInterfaceComponentReference;
begin
if Supports(FInterfaceToAService, IInterfaceComponentReference, comRef) then
comRef.GetComponent.RemoveFreeNotification(self);
FInterfaceToAService := Value;
if Supports(FInterfaceToAService, IInterfaceComponentReference, comRef) then
comRef.GetComponent.FreeNotification(self);
end;