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;