代码之家  ›  专栏  ›  技术社区  ›  Peter-John Jansen

ARC下的数据库连接对象(Mydac TMyConnection)会发生什么

  •  1
  • Peter-John Jansen  · 技术社区  · 9 年前

    我在ARC下做过内存管理的研究 但我仍然不确定在这种情况下会发生什么

    function foo() : boolean
    var
        Mycon : TMyConnection
        MyQuery : TMyQuery
    begin
        Mycon := TMyConnection.Create(nil);
        Mycon.ConnectString := MyConnection1.ConnectString;
        Mycon.ConnectionTimeout:= 3;
        MyQuery := TMyQuery.Create(nil);
        MyQuery.Connection := Mycon;
        Mycon.Connect;
    
        //Do a few Queries
    end;
    

    现在传统上,我会调用Free来释放它们,但我知道ARC使用引用计数来释放对象, 一旦对象超出范围,它就会被释放,在本例中是在查询被释放之后。

    现在我的问题是:TMyConnection的活动连接是否会将对象保持在范围内?

    我知道我总是可以将Mycon分配给NIL或调用DisposeOf来打破任何引用。

    1 回复  |  直到 9 年前
        1
  •  3
  •   Community TheSoundDefense    7 年前

    我知道ARC使用引用计数来释放对象,一旦对象超出范围,它就会被释放

    更准确地说,当它的引用计数降至0时,它将被释放。这是一个很大的区别,因为如果仍然有其他对它的活动引用,变量可能会超出作用域而不释放对象本身。

    TMyConnection的活动连接是否会将对象保持在范围内?

    这取决于 基于几个因素:

    1. 是否 TMyQuery.Connection 财产用途 坚强的 虚弱的 参考 TMyConnection 对象(即 TMyQuery 支持它的字段 Connection 属性具有 [weak] 属性)。

    2. 是否 TMyQuery.连接 属性setter调用 FreeNotification() TMy连接 对象

    让我们看看最佳情况- 虚弱的 引用和否 免费通知() :

    type
      TMyConnection = class(...)
        //...
      end;
    
      TMyQuery = class(...)
      private
        [weak] FConn: TMyConnection;
      published
        property Connection: TMyConnection read FConn write FConn;
      end;
    

    function foo() : boolean
    var
      Mycon : TMyConnection
      MyQuery : TMyQuery
    begin
      Mycon := TMyConnection.Create(nil); // <-- MyCon.RefCnt is now 1
      Mycon.ConnectString := MyConnection1.ConnectString;
      Mycon.ConnectionTimeout:= 3;
      MyQuery := TMyQuery.Create(nil); // <-- MyQuery.RefCnt is now 1
      MyQuery.Connection := Mycon; // <-- MyCon.RefCnt remains 1
      Mycon.Connect;
    
      *Do a few Queries*
    
    end; // <-- MyCon.RefCnt drops to 0, MyQuery.RefCnt drops to 0, OK!
    

    在这种情况下,由于 TMyQuery 对象具有 虚弱的 参考 TMy连接 对象 TMy连接 的引用计数不递增。因此,当 MyCon MyQuery 变量超出范围,两个对象都被释放。

    现在让我们看看更糟糕的情况- 坚强的 参考和 免费通知() 。如果您将组件从预ARC版本迁移到基于ARC的系统,而不重新编写组件以处理ARC,则可能会遇到以下情况:

    type
      TMyConnection = class(...)
        //...
      end;
    
      TMyQuery = class(...)
      private
        FConn: TMyConnection;
        procedure SetConnection(AValue: TMyConnection);
      protected
        procedure Notification(AComponent: TComponent; Operation: TOperation); override;
      published
        property Connection: TMyConnection read FConn write SetConnection;
      end;
    
    procedure TMyQuery.Notification(AComponent: TComponent; Operation: TOperation);
    begin
      inherited;
      if (Operation = opRemove) and (AComponent = FConn) then
        FConn := nil;
    end;
    
    procedure TMyQuery.SetConnection(AValue: TMyConnection);
    begin
      if FConn <> AValue then
      begin
        if FConn <> nil then FConn.RemoveFreeNotification(Self);
        FConn := AValue;
        if FConn <> nil then FConn.FreeNotification(Self);
      end;
    end;
    

    function foo() : boolean
    var
      Mycon : TMyConnection
      MyQuery : TMyQuery
    begin
      Mycon := TMyConnection.Create(nil); // <-- MyCon.RefCnt is now 1
      Mycon.ConnectString := MyConnection1.ConnectString;
      Mycon.ConnectionTimeout:= 3;
      MyQuery := TMyQuery.Create(nil); // <-- MyQuery.RefCnt is now 1
      MyQuery.Connection := Mycon; // <-- MyCon.RefCnt is now 3, MyQuery.RefCnt is now 2
      Mycon.Connect;
    
      *Do a few Queries*
    
    end; // <-- MyCon.RefCnt drops to 2, MyQuery.RefCnt drops to 1, LEAKS!
    

    在这种情况下,由于 TMyQuery 对象有2个 坚强的 TMy连接 对象(1表示支持 联系 属性中的1 免费通知() 列表),以及 TMy连接 有一个 坚强的 参考 TMyQuery (在其 免费通知() 列表),当 MyCon公司 我的查询 变量超出范围,因此这两个对象都被泄漏。

    为什么 免费通知() 列表有 坚强的 参考?因为在XE3中,Embarcadero改变了 TComponent.FFreeNotifies 来自的成员 TList TList<TComponent> 。列表中的指针现在已键入,无法标记 TList<T> T 源自 TObject ,因此他们使用了强引用。这是Embarcadero尚未解决的已知问题,因为XE8仍在使用 TList<T组分> 而不是回到 TL列表 或至少切换到 TList<Pointer> .

    有关详细信息,请参阅此问题的答案:

    How to free a component in Android / iOS

    我知道我总是可以将Mycon分配给NIL或调用DisposeOf来打破任何引用。

    背景 MyCon公司 设置为nil只会释放该特定引用,但不会对其他引用产生任何影响,例如 TMyQuery.连接 使命感 MyCon.DisposeOf() 另一方面,将释放对象并保留所有 坚强的 非nil中的引用 Disposed 状态

    然而,在这种情况下,您应该能够清除 MyQuery.Connection 让它有机会释放任何 坚强的 它可能引用 TMy连接 对象这在上述两种情况下都有效:

    // weak referencing, no FreeNotifcation():
    
    function foo() : boolean
    var
      Mycon : TMyConnection
      MyQuery : TMyQuery
    begin
      Mycon := TMyConnection.Create(nil); // <-- MyCon.RefCnt is now 1
      Mycon.ConnectString := MyConnection1.ConnectString;
      Mycon.ConnectionTimeout:= 3;
      MyQuery := TMyQuery.Create(nil); // <-- MyQuery.RefCnt is now 1
      MyQuery.Connection := Mycon; // <-- MyCon.RefCnt remains 1, MyQuery.RefCnt remains 1
      try
        Mycon.Connect;
        *Do a few Queries*
      finally
        MyQuery.Connection := nil; // <-- MyCon.RefCnt remains 1, MyQuery.RefCnt remains 1
      end;
    end; // <-- MyCon.RefCnt drops to 0, MyQuery.RefCnt drops to 0, OK!
    

    // strong reference, FreeNotification():
    
    function foo() : boolean
    var
      Mycon : TMyConnection
      MyQuery : TMyQuery
    begin
      Mycon := TMyConnection.Create(nil); // <-- MyCon.RefCnt is now 1
      Mycon.ConnectString := MyConnection1.ConnectString;
      Mycon.ConnectionTimeout:= 3;
      MyQuery := TMyQuery.Create(nil); // <-- MyQuery.RefCnt is now 1
      MyQuery.Connection := Mycon; // <-- MyCon.RefCnt is now 3, MyQuery.RefCnt is now 2
      try
        Mycon.Connect;
        *Do a few Queries*
      finally
        MyQuery.Connection := nil; // <-- MyCon.RefCnt drops to 1, MyQuery.RefCnt drops to 1
      end;
    end; // <-- MyCon.RefCnt drops to 0, MyQuery.RefCnt drops to 0, OK!