今天测试了一些东西时,我遇到了一个奇怪的情况。Delphi界面参考计数
我有一些接口和对象。代码如下所示:
IInterfaceZ = interface(IInterface)
['{DA003999-ADA2-47ED-A1E0-2572A00B6D75}']
procedure DoSomething;
end;
IInterfaceY = interface(IInterface)
['{55BF8A92-FCE4-447D-B58B-26CD9B344EA7}']
procedure DoNothing;
end;
TObjectB = class(TInterfacedObject, IInterfaceZ)
procedure DoSomething;
end;
TObjectC = class(TInterfacedObject, IInterfaceY)
public
FTest: string;
procedure DoNothing;
end;
TObjectA = class(TInterfacedObject, IInterfaceZ, IInterfaceY)
private
FInterfaceB: IInterfaceZ;
FObjectC: TObjectC;
function GetBB: IInterfaceZ;
public
procedure AfterConstruction; override;
procedure BeforeDestruction; override;
property BB: IInterfaceZ read GetBB implements IInterfaceZ;
property CC: TObjectC read FObjectC implements IInterfaceY;
end;
procedure TObjectB.DoSomething;
begin
Sleep(1000);
end;
procedure TObjectA.AfterConstruction;
begin
inherited;
FInterfaceB := TObjectB.Create;
FObjectC := TObjectC.Create;
FObjectC.FTest := 'Testing';
end;
procedure TObjectA.BeforeDestruction;
begin
FreeAndNil(FObjectC);
FInterfaceB := nil;
inherited;
end;
function TObjectA.GetBB: IInterfaceZ;
begin
Result := FInterfaceB;
end;
procedure TObjectC.DoNothing;
begin
ShowMessage(FTest);
end;
现在,如果我访问各种实现这样我得到如下结果:
procedure TestInterfaces;
var
AA: TObjectA;
YY: IInterfaceY;
ZZ: IInterfaceZ;
NewYY: IInterfaceY;
begin
AA := TObjectA.Create;
// Make sure that the Supports doesn't kill the object.
// This line of code is necessary in XE2 but not in XE4
AA._AddRef;
// This will add one to the refcount for AA despite the fact
// that AA has delegated the implementation of IInterfaceY to
// to FObjectC.
Supports(AA, IInterfaceY, YY);
YY.DoNothing;
// This will add one to the refcount for FInterfaceB.
// This is also allowing a supports from a delegated interface
// to another delegated interface.
Supports(YY, IInterfaceZ, ZZ);
ZZ.DoSomething;
// This will fail because the underlying object is actually
// the object referenced by FInterfaceB.
Supports(ZZ, IInterfaceY, NewYY);
NewYY.DoNothing;
end;
第一个支持呼叫,它采用可变的工具,返回YY这实际上是对TObjectA的引用。我的AA变量是参考计数。因为底层引用计数对象是一个TObjectA,所以第二个支持使用支持调用中的接口,它工作并返回一个接口。底层对象实际上现在是一个TObjectB。 FInterfaceB后面的内部对象是被引用计数的对象。这部分是有意义的,因为GetBB实际上是FInterfaceB。正如预期的那样,最后一次对Supports的调用返回NewYY的空值,并且最后的调用失败。
我的问题是这个,引用第一个支持设计的支持电话是否引用TObjectA?换句话说,当实现接口的属性返回一个对象而不是一个接口时,这是否意味着所有者对象将成为引用计数的对象?我一直认为,实现也会导致内部委托对象被引用计数而不是主要对象。
的声明如下:
property BB: IInterfaceZ read GetBB implements IInterfaceZ;
通过上述这个选项,后面FInterfaceB内部对象是一个是引用计数。
property CC: TObjectC read FObjectC implements IInterfaceY;
利用上述第二种选择,TObjectA是正在引用计数,而不是委托对象FObjectC之一。
这是设计吗?
编辑
我只是在XE2测试这和行为是不同的。第二个支持声明返回零为ZZ。 XE4中的调试器告诉我YY指的是(TObjectA作为IInterfaceY)。在XE2中它告诉我它的一个(指针为IInterfaceY)。另外,在XE2中,AA不是在第一个支持语句中被引用,而是内部FObjectC是引用计数。
问题后附加信息回答
有一点需要注意这一点。您可以链接接口版本,但不链接对象版本。这意味着,像这样将工作:
TObjectBase = class(TInterfacedObject, IMyInterface)
…
end;
TObjectA = class(TInterfacedObject, IMyInterface)
FMyInterfaceBase: IMyInterface;
property MyDelegate: IMyInterface read GetMyInterface implements IMyInterface;
end;
function TObjectA.GetMyInterface: IMyInterface;
begin
result := FMyInterfaceBase;
end;
TObjectB = class(TInterfacedObject, IMyInterface)
FMyInterfaceA: IMyInterface;
function GetMyInterface2: IMyInterface;
property MyDelegate2: IMyInterface read GetMyInterface2 implements IMyInterface;
end;
function TObjectB.GetMyInterface2: IMyInterface;
begin
result := FMyInterfaceA;
end;
但对象版本给出了编译器错误此说TObjectB没有实现该接口的方法。
TObjectBase = class(TInterfacedObject, IMyInterface)
…
end;
TObjectA = class(TInterfacedObject, IMyInterface)
FMyObjectBase: TMyObjectBase;
property MyDelegate: TMyObjectBase read FMyObjectBase implements IMyInterface;
end;
TObjectB = class(TInterfacedObject, IMyInterface)
FMyObjectA: TObjectA;
property MyDelegate2: TObjectA read FMyObjectA implements IMyInterface;
end;
所以,如果你想开始链接委托,那么你需要坚持接口或以另一种方式解决它。
你对TObjectA只有很弱的参考,因此你会失去AA,剩下的就是AA。 –
@SirRufo我明白,这仅仅是一个例子。我更关心引用计数是如何完成的。基本上哪个对象被引用计数。 – Graymatter
只需重写_AddRef/_Release方法调用并记录它们以及主程序,然后就可以看到它。 –