我已经对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指定为0,或者打电话给DisposeOf来打破任何拒绝。
发布于 2015-08-14 20:38:40
我知道ARC对空闲对象使用引用计数,一旦超出作用域,对象就会被释放。
更准确地说,当它的引用计数降到0时,就会释放它。差别很大,因为如果仍然有其他的活动引用,变量可能会超出作用域,而不释放对象本身。
TMyConnection的活动连接会保持对象的作用域吗?
--它依赖于的几个因素:
TMyQuery.Connection属性是否使用对TMyConnection对象的强引用或弱引用(即,支持其Connection属性的TMyQuery字段是否具有[weak]属性)。TMyQuery.Connection属性设置器是否在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,并且两个对象都被释放。
现在让我们来看看更糟的情况--强引用和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对象有两个对TMyConnection对象的强引用(一个用于支持Connection属性的字段,一个用于FreeNotification()列表),而TMyConnection具有对TMyQuery的强引用(在其FreeNotification()列表中),因此两个对象引用计数都会增加,并且在MyCon和MyQuery变量超出作用域时不会下降到0,因此这两个对象都会泄漏。
为什么FreeNotification()列表有很强的引用?因为在XE3中,Embarcadero将TComponent.FFreeNotifies成员从TList更改为TList<TComponent>。列表中的指针现在已经输入,当TList<T>从TObject派生出来时,没有方法将T标记为保持弱指针,因此它们使用强引用。这是Embarcadero尚未解决的一个已知问题,因为XE8仍然在使用TList<TComponent>,而不是返回到TList,或者至少切换到TList<Pointer>。
有关详细信息,请参阅此问题的答案:
How to free a component in Android / iOS
我知道我总是可以把Mycon指定为0,或者打电话给DisposeOf来打破任何拒绝。
将MyCon设置为零只会释放特定的引用,但不会对其他引用(如TMyQuery.Connection )产生任何影响。另一方面,调用MyCon.DisposeOf()将释放对象,并使所有强引用处于非零Disposed状态。
但是,在这种情况下,您应该能够清除MyQuery.Connection属性,让它有机会释放对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!https://stackoverflow.com/questions/32010583
复制相似问题