Delphi "Supports" 增加 [weak] 或 [unsafe] 接口上的引用计数
Delphi "Supports" increments reference count on [weak] or [unsafe] interfaces
当我使用 Delphi Berlin 10.1 [weak](和 [unsafe])引用时,"Supports" 函数和 "QueryInterface" 都在给定接口时增加引用计数用 "weak" 属性标记的变量(与 "unsafe" 属性相同的行为)。
program WeakReferences;
{$APPTYPE CONSOLE}
{$R *.res}
uses System.SysUtils;
type
IAnInterfacedObject = interface
['{351DFDA3-42CA-4A1D-8488-494CA454FD9C}']
end;
TAnInterfacedObject = class(TInterfacedObject, IAnInterfacedObject)
protected
function GetTheReferenceCount : integer;
public
constructor Create;
destructor Destroy; override;
property TheReferenceCount : integer read GetTheReferenceCount;
end;
constructor TAnInterfacedObject.Create;
begin
inherited Create;
writeln('(create AIO instance)');
end;
destructor TAnInterfacedObject.Destroy;
begin
writeln('(destroy AIO instance)');
inherited Destroy;
end;
function TAnInterfacedObject.GetTheReferenceCount : integer;
begin
Result := FRefCount;
end;
procedure WithoutSupports;
var
AIOinstance : TAnInterfacedObject;
AIOinterfaced : IAnInterfacedObject;
[Weak]
WeakAIOinterfaced : IAnInterfacedObject;
begin
AIOinstance := TAnInterfacedObject.Create;
writeln('created AIO instance; refcount: '+AIOinstance.TheReferenceCount.ToString);
AIOinterfaced := AIOinstance;
writeln('create AIO interfaced; refcount: '+AIOinstance.TheReferenceCount.ToString);
WeakAIOinterfaced := AIOinstance;
writeln('create WEAK AIO interfaced; refcount: '+AIOinstance.TheReferenceCount.ToString);
end;
procedure WithSupports_Weak;
var
AIOinstance : TAnInterfacedObject;
AIOinterfaced : IAnInterfacedObject;
[Weak]
WeakAIOinterfaced : IAnInterfacedObject;
begin
AIOinstance := TAnInterfacedObject.Create;
writeln('created AIO instance; refcount: '+AIOinstance.TheReferenceCount.ToString);
AIOinterfaced := AIOinstance;
writeln('create AIO interfaced; refcount: '+AIOinstance.TheReferenceCount.ToString);
Supports(AIOinstance, IAnInterfacedObject, WeakAIOinterfaced);
writeln('create WEAK AIO interfaced with SUPPORTS; refcount: '+AIOinstance.TheReferenceCount.ToString);
end;
procedure WithSupports_Unsafe;
var
AIOinstance : TAnInterfacedObject;
AIOinterfaced : IAnInterfacedObject;
[Unsafe]
UnsafeAIOinterfaced : IAnInterfacedObject;
begin
AIOinstance := TAnInterfacedObject.Create;
writeln('created AIO instance; refcount: '+AIOinstance.TheReferenceCount.ToString);
AIOinterfaced := AIOinstance;
writeln('create AIO interfaced; refcount: '+AIOinstance.TheReferenceCount.ToString);
Supports(AIOinstance, IAnInterfacedObject, UnsafeAIOinterfaced);
writeln('create UNSAFE AIO interfaced with SUPPORTS; refcount: '+AIOinstance.TheReferenceCount.ToString);
end;
procedure WithQueryInterface_Weak;
var
AIOinstance : TAnInterfacedObject;
AIOinterfaced : IAnInterfacedObject;
[Weak]
WeakAIOinterfaced : IAnInterfacedObject;
begin
AIOinstance := TAnInterfacedObject.Create;
writeln('created AIO instance; refcount: '+AIOinstance.TheReferenceCount.ToString);
AIOinterfaced := AIOinstance;
writeln('create AIO interfaced; refcount: '+AIOinstance.TheReferenceCount.ToString);
AIOinterfaced.QueryInterface(IAnInterfacedObject, WeakAIOinterfaced);
writeln('create WEAK AIO interfaced with QUERYINTERFACE; refcount: '+AIOinstance.TheReferenceCount.ToString);
end;
begin
try
writeln('--Without "Supports"-------------------');
WithoutSupports;
writeln;
writeln('--With "Supports" - weak-------------------');
WithSupports_Weak;
writeln;
writeln('--With "Supports" - unsafe-------------------');
WithSupports_Unsafe;
writeln;
writeln('--With "QueryInterface" - weak-------------------');
WithQueryInterface_Weak;
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
readln;
end.
我在这里错过了什么?有"WeakSupports"函数吗?这是新 "weak" 界面功能的错误还是缺点?
Delphi Supports
(其中一个重载 - 但考虑到 out
参数,所有其他重载都是相同的)函数声明为
function Supports(const Instance: IInterface; const IID: TGUID; out Intf): Boolean;
和QueryInterface
作为
function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall;
两个 out
参数都没有标记为 [weak]
。这意味着您不能将 [weak]
或 [unsafe]
接口引用传递给它们。您只能传递对此类参数的强引用以保持引用计数的顺序。
来自文档 Weak References:
Note: You can only pass a [Weak] variable to a var or out parameter
that is also marked as [Weak]. You cannot pass a regular strong
reference to a [Weak] var or out parameter.
Note: You can only pass an [Unsafe] variable to a var or out parameter
that is also marked as [Unsafe]. You cannot pass a regular strong
reference to an [Unsafe] var or out parameter.
此外,您的测试用例还有另一个问题。除非你使用 full-ARC Delphi 编译器(目前 Android 和 iOS)你不能 store 引用计数对象到对象引用或者你会搞乱引用计数。更准确地说,non-ARC 编译器下的对象引用对引用计数没有贡献,您只能将它们用作临时访问点,对无法通过声明的接口访问的对象执行一些操作。您应该始终至少有一个对该对象实例的接口引用,以保持引用计数的顺序,并防止过早的对象破坏或内存泄漏。
而不是
var
AIOinstance : TAnInterfacedObject;
...
AIOinstance := TAnInterfacedObject.Create;
你应该总是使用
var
AIOinstance : IAnInterfacedObject;
...
AIOinstance := TAnInterfacedObject.Create;
当我使用 Delphi Berlin 10.1 [weak](和 [unsafe])引用时,"Supports" 函数和 "QueryInterface" 都在给定接口时增加引用计数用 "weak" 属性标记的变量(与 "unsafe" 属性相同的行为)。
program WeakReferences;
{$APPTYPE CONSOLE}
{$R *.res}
uses System.SysUtils;
type
IAnInterfacedObject = interface
['{351DFDA3-42CA-4A1D-8488-494CA454FD9C}']
end;
TAnInterfacedObject = class(TInterfacedObject, IAnInterfacedObject)
protected
function GetTheReferenceCount : integer;
public
constructor Create;
destructor Destroy; override;
property TheReferenceCount : integer read GetTheReferenceCount;
end;
constructor TAnInterfacedObject.Create;
begin
inherited Create;
writeln('(create AIO instance)');
end;
destructor TAnInterfacedObject.Destroy;
begin
writeln('(destroy AIO instance)');
inherited Destroy;
end;
function TAnInterfacedObject.GetTheReferenceCount : integer;
begin
Result := FRefCount;
end;
procedure WithoutSupports;
var
AIOinstance : TAnInterfacedObject;
AIOinterfaced : IAnInterfacedObject;
[Weak]
WeakAIOinterfaced : IAnInterfacedObject;
begin
AIOinstance := TAnInterfacedObject.Create;
writeln('created AIO instance; refcount: '+AIOinstance.TheReferenceCount.ToString);
AIOinterfaced := AIOinstance;
writeln('create AIO interfaced; refcount: '+AIOinstance.TheReferenceCount.ToString);
WeakAIOinterfaced := AIOinstance;
writeln('create WEAK AIO interfaced; refcount: '+AIOinstance.TheReferenceCount.ToString);
end;
procedure WithSupports_Weak;
var
AIOinstance : TAnInterfacedObject;
AIOinterfaced : IAnInterfacedObject;
[Weak]
WeakAIOinterfaced : IAnInterfacedObject;
begin
AIOinstance := TAnInterfacedObject.Create;
writeln('created AIO instance; refcount: '+AIOinstance.TheReferenceCount.ToString);
AIOinterfaced := AIOinstance;
writeln('create AIO interfaced; refcount: '+AIOinstance.TheReferenceCount.ToString);
Supports(AIOinstance, IAnInterfacedObject, WeakAIOinterfaced);
writeln('create WEAK AIO interfaced with SUPPORTS; refcount: '+AIOinstance.TheReferenceCount.ToString);
end;
procedure WithSupports_Unsafe;
var
AIOinstance : TAnInterfacedObject;
AIOinterfaced : IAnInterfacedObject;
[Unsafe]
UnsafeAIOinterfaced : IAnInterfacedObject;
begin
AIOinstance := TAnInterfacedObject.Create;
writeln('created AIO instance; refcount: '+AIOinstance.TheReferenceCount.ToString);
AIOinterfaced := AIOinstance;
writeln('create AIO interfaced; refcount: '+AIOinstance.TheReferenceCount.ToString);
Supports(AIOinstance, IAnInterfacedObject, UnsafeAIOinterfaced);
writeln('create UNSAFE AIO interfaced with SUPPORTS; refcount: '+AIOinstance.TheReferenceCount.ToString);
end;
procedure WithQueryInterface_Weak;
var
AIOinstance : TAnInterfacedObject;
AIOinterfaced : IAnInterfacedObject;
[Weak]
WeakAIOinterfaced : IAnInterfacedObject;
begin
AIOinstance := TAnInterfacedObject.Create;
writeln('created AIO instance; refcount: '+AIOinstance.TheReferenceCount.ToString);
AIOinterfaced := AIOinstance;
writeln('create AIO interfaced; refcount: '+AIOinstance.TheReferenceCount.ToString);
AIOinterfaced.QueryInterface(IAnInterfacedObject, WeakAIOinterfaced);
writeln('create WEAK AIO interfaced with QUERYINTERFACE; refcount: '+AIOinstance.TheReferenceCount.ToString);
end;
begin
try
writeln('--Without "Supports"-------------------');
WithoutSupports;
writeln;
writeln('--With "Supports" - weak-------------------');
WithSupports_Weak;
writeln;
writeln('--With "Supports" - unsafe-------------------');
WithSupports_Unsafe;
writeln;
writeln('--With "QueryInterface" - weak-------------------');
WithQueryInterface_Weak;
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
readln;
end.
我在这里错过了什么?有"WeakSupports"函数吗?这是新 "weak" 界面功能的错误还是缺点?
Delphi Supports
(其中一个重载 - 但考虑到 out
参数,所有其他重载都是相同的)函数声明为
function Supports(const Instance: IInterface; const IID: TGUID; out Intf): Boolean;
和QueryInterface
作为
function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall;
两个 out
参数都没有标记为 [weak]
。这意味着您不能将 [weak]
或 [unsafe]
接口引用传递给它们。您只能传递对此类参数的强引用以保持引用计数的顺序。
来自文档 Weak References:
Note: You can only pass a [Weak] variable to a var or out parameter that is also marked as [Weak]. You cannot pass a regular strong reference to a [Weak] var or out parameter.
Note: You can only pass an [Unsafe] variable to a var or out parameter that is also marked as [Unsafe]. You cannot pass a regular strong reference to an [Unsafe] var or out parameter.
此外,您的测试用例还有另一个问题。除非你使用 full-ARC Delphi 编译器(目前 Android 和 iOS)你不能 store 引用计数对象到对象引用或者你会搞乱引用计数。更准确地说,non-ARC 编译器下的对象引用对引用计数没有贡献,您只能将它们用作临时访问点,对无法通过声明的接口访问的对象执行一些操作。您应该始终至少有一个对该对象实例的接口引用,以保持引用计数的顺序,并防止过早的对象破坏或内存泄漏。
而不是
var
AIOinstance : TAnInterfacedObject;
...
AIOinstance := TAnInterfacedObject.Create;
你应该总是使用
var
AIOinstance : IAnInterfacedObject;
...
AIOinstance := TAnInterfacedObject.Create;