我知道ARC使用引用计数来释放对象,一旦对象超出范围,它就会被释放
更准确地说,当它的引用计数降至0时,它将被释放。这是一个很大的区别,因为如果仍然有其他对它的活动引用,变量可能会超出作用域而不释放对象本身。
TMyConnection的活动连接是否会将对象保持在范围内?
这取决于
基于几个因素:
-
是否
TMyQuery.Connection
财产用途
坚强的
或
虚弱的
参考
TMyConnection
对象(即
TMyQuery
支持它的字段
Connection
属性具有
[weak]
属性)。
-
是否
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!