【问题标题】:Delphi "Supports" increments reference count on [weak] or [unsafe] interfacesDelphi“支持”增加[弱]或[不安全]接口的引用计数
【发布时间】:2017-02-17 16:44:38
【问题描述】:

当我使用 Delphi Berlin 10.1 [weak](和 [unsafe])引用时,当给定一个标有“weak”属性的接口变量时,“Supports”函数和“QueryInterface”都会增加引用计数(相同具有“不安全”属性的行为)。

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.

我在这里错过了什么?是否有“弱支持”功能?这是一个错误还是只是新的“弱”接口功能的一个缺点?

【问题讨论】:

    标签: delphi weak-references delphi-10.1-berlin


    【解决方案1】:

    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

    注意:您只能将 [Weak] 变量传递给 var 或 out 参数 也被标记为[弱]。你不能通过一个普通的强 引用 [Weak] var 或 out 参数。

    注意:您只能将 [Unsafe] 变量传递给 var 或 out 参数 也被标记为 [不安全]。你不能通过一个普通的强 引用 [Unsafe] var 或 out 参数。


    此外,您的测试用例还有另一个问题。除非您使用的是完整的 ARC Delphi 编译器(目前是 Android 和 iOS),否则您不能将引用计数的对象存储到对象引用,否则您会弄乱引用计数。更准确地说,非ARC编译器下的对象引用对引用计数没有贡献,你只能将它们用作临时访问点,对不能通过声明的接口访问的对象执行一些操作。您应该始终至少有一个对该对象实例的接口引用,以保持引用计数有序,并防止对象过早破坏或内存泄漏。

    代替

     var
       AIOinstance : TAnInterfacedObject;
       ...
       AIOinstance := TAnInterfacedObject.Create;
    

    你应该总是使用

    var
      AIOinstance : IAnInterfacedObject;
      ...
      AIOinstance := TAnInterfacedObject.Create;
    

    【讨论】:

    • @ArnaudBouchez 我不确定你的意思。混合对象引用和接口引用问题正是 ARC 编译器解决的问题。当然,对于跨平台代码,您仍然必须遵循非 ARC 编译器规则,切勿将这两者混为一谈。
    • Dalija,有正当理由使用本地对象实例创建对象,然后将其分配给接口。我们经常创建并初始化一个对象,然后才将其分配给一个接口。
    • 我不是说保留对象,而是说使用它来创建和设置对象。例如,函数返回 IMyObject 的begin MyObj := TMyObject.Create; MyObj.Init(xxxx); Result := MyObj; end; - 您最后的评论“您应该始终使用”不一定实用。当然,您可以创建一个接口来设置您的对象,但这是不必要的。
    • 我遗漏了一些东西。您将如何获得不正确的参考计数?这是工厂非常常见的模式。 pastebin.com/nYWgdfSe
    • 其实我同意@Graymatter 的观点,有时我会初始化一个类实例,然后调用一些调优方法,然后将该类分配到一个接口中,以供抽象使用。这在使用 SOLID 代码时很常见。
    猜你喜欢
    • 2021-08-17
    • 2014-05-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-03-16
    • 1970-01-01
    • 2011-03-27
    相关资源
    最近更新 更多