ARC下的数据库连接对象(Mydac TMyConnection)发生了什么
What happens to Database connection objects(Mydac TMyConnection) under ARC
我研究过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 来打破任何引用。
I know ARC uses reference counting to free objects, And a object is freed once it goes out of scope
更准确地说,当它的引用计数降为 0 时,它会被释放。差别很大,因为如果仍有其他活动引用,变量可能会超出范围而对象本身不会被释放。
Would the active connection of the TMyConnection keep the object in scope?
这取决于几个因素:
TMyQuery.Connection
属性 是否使用 strong 或 weak 引用 TMyConnection
对象(即支持其 Connection
属性 的 TMyQuery
字段是否具有 [weak]
属性)。
TMyQuery.Connection
属性setter是否在TMyConnection
对象上调用FreeNotification()
。
让我们看看最好的情况 - 弱 引用并且没有 FreeNotification()
:
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
对象具有对 TMyConnection
对象的 弱 引用,因此 TMyConnection
的引用计数不递增。因此,当 MyCon
和 MyQuery
变量超出范围时,两个对象引用计数都降为 0,并且两个对象都被释放。
现在让我们看看最坏的情况 - strong 引用和 FreeNotification()
。如果您将组件从 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 个 strong 对 TMyConnection
对象的引用(1 个用于支持 Connection
属性,并在其 FreeNotification()
列表中有 1 个),并且 TMyConnection
具有对 TMyQuery
的 strong 引用(在其 FreeNotification()
列表),当 MyCon
和 MyQuery
变量超出范围时,两个对象引用计数都会递增并且不会下降到 0,因此两个对象都会泄漏。
为什么 FreeNotification()
列表有 strong 引用?因为在 XE3 中,Embarcadero 将 TComponent.FFreeNotifies
成员从 TList
更改为 TList<TComponent>
。列表中的指针现在是类型化的,当 T
派生自 TObject
时,无法将 TList<T>
标记为持有弱指针,因此它们使用强引用。这是 Embarcadero 尚未解决的已知问题,因为 XE8 仍在使用 TList<TComponent>
而不是返回 TList
或至少切换到 TList<Pointer>
.
查看此问题的答案了解更多详情:
How to free a component in Android / iOS
I know I could always just assign Mycon to NIL or call DisposeOf to break any refrences.
将 MyCon
设置为 nil 只会释放该特定引用,但不会对其他引用产生任何影响,例如 TMyQuery.Connection
。另一方面,调用 MyCon.DisposeOf()
会释放对象并使所有 strong 引用处于非零 Disposed
状态。
然而,在这种情况下,您应该能够清除 MyQuery.Connection
属性 以使其有机会释放任何 strong它可能必须引用 TMyConnection
对象。这适用于上述两种情况:
// 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!
我研究过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 来打破任何引用。
I know ARC uses reference counting to free objects, And a object is freed once it goes out of scope
更准确地说,当它的引用计数降为 0 时,它会被释放。差别很大,因为如果仍有其他活动引用,变量可能会超出范围而对象本身不会被释放。
Would the active connection of the TMyConnection keep the object in scope?
这取决于几个因素:
TMyQuery.Connection
属性 是否使用 strong 或 weak 引用TMyConnection
对象(即支持其Connection
属性 的TMyQuery
字段是否具有[weak]
属性)。TMyQuery.Connection
属性setter是否在TMyConnection
对象上调用FreeNotification()
。
让我们看看最好的情况 - 弱 引用并且没有 FreeNotification()
:
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
对象具有对 TMyConnection
对象的 弱 引用,因此 TMyConnection
的引用计数不递增。因此,当 MyCon
和 MyQuery
变量超出范围时,两个对象引用计数都降为 0,并且两个对象都被释放。
现在让我们看看最坏的情况 - strong 引用和 FreeNotification()
。如果您将组件从 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 个 strong 对 TMyConnection
对象的引用(1 个用于支持 Connection
属性,并在其 FreeNotification()
列表中有 1 个),并且 TMyConnection
具有对 TMyQuery
的 strong 引用(在其 FreeNotification()
列表),当 MyCon
和 MyQuery
变量超出范围时,两个对象引用计数都会递增并且不会下降到 0,因此两个对象都会泄漏。
为什么 FreeNotification()
列表有 strong 引用?因为在 XE3 中,Embarcadero 将 TComponent.FFreeNotifies
成员从 TList
更改为 TList<TComponent>
。列表中的指针现在是类型化的,当 T
派生自 TObject
时,无法将 TList<T>
标记为持有弱指针,因此它们使用强引用。这是 Embarcadero 尚未解决的已知问题,因为 XE8 仍在使用 TList<TComponent>
而不是返回 TList
或至少切换到 TList<Pointer>
.
查看此问题的答案了解更多详情:
How to free a component in Android / iOS
I know I could always just assign Mycon to NIL or call DisposeOf to break any refrences.
将 MyCon
设置为 nil 只会释放该特定引用,但不会对其他引用产生任何影响,例如 TMyQuery.Connection
。另一方面,调用 MyCon.DisposeOf()
会释放对象并使所有 strong 引用处于非零 Disposed
状态。
然而,在这种情况下,您应该能够清除 MyQuery.Connection
属性 以使其有机会释放任何 strong它可能必须引用 TMyConnection
对象。这适用于上述两种情况:
// 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!