我正在使用Spring4D在Delphi 10.3 Rio中测试智能指针。这是我的测试程序。我创建了一个通用的TObjectList
,并想使用TObject
将简单的Shared.Make(TTestObj.Create)
添加到此列表中。问题在于,每当我将一个对象添加到列表中时,就会释放前一个对象。请参阅我的程序的输出。有谁知道如何解决这个问题?
program TestSmartPointer;
{$APPTYPE CONSOLE}
uses
Spring,
Diagnostics,
Classes,
SysUtils,
System.Generics.Collections;
type
TTestObj = class
private
FDescription: string;
public
property Description: string read FDescription write FDescription;
destructor Destroy; override;
end;
TTestList = class(TObjectList<TTestObj>)
destructor Destroy; override;
end;
var
LISTITEMCOUNT: integer;
LISTCOUNT: integer;
procedure Test_SmartPointer;
begin
Writeln('SmartPointer test started');
var lTestList := Shared.Make(TTestList.Create)();
lTestList.OwnsObjects := false;
for var i := 1 to 10 do
begin
var lTestObj := Shared.Make(TTestObj.Create)();
// var lTestObj := TTestObj.Create;
lTestObj.Description := i.ToString;
Writeln('TestObj added to Testlist with description ' + lTestObj.Description);
lTestList.Add(lTestObj);
end;
Writeln('SmartPointer test finished');
end;
{ TTestObj }
destructor TTestObj.Destroy;
begin
Writeln(format('TTestObj with description %s is destroyed', [FDescription]));
inherited;
end;
{ TTestList }
destructor TTestList.Destroy;
begin
Writeln('TTestList is destroyed');
inherited;
end;
begin
Test_SmartPointer;
Readln;
end.
问题是您的TObjectList
保留原始TTestObj
对象指针,而不是IShared<TTestObj>
返回的Shared.Make<T>()
接口。
在var lTestList := Shared.Make(TTestList.Create)();
中,您正在创建一个IShared<TTestList>
(reference to function: TTestList
),该文件包装了您正在创建的TTestList
对象。您正在调用()
上的IShared
,这将调用该函数以返回原始TTestList
对象指针。在此示例中可以,因为IShared
将在Test_SmartPointer()
的生存期内保存在一个隐藏变量中,因此其refcount为1,从而使TTestList
保持活动状态。
在var lTestObj := Shared.Make(TTestObj.Create)();
中,您正在执行相同的操作,这次是IShared<TTestObj>
返回TTestObj
对象指针。但是,当lTestObj
在每次循环迭代结束时超出范围时,IShared
的引用计数将减少。由于没有对该接口的进一步引用,它的refcount降为0,从而破坏了IShared
后面的对象,这又破坏了其关联的TTestObj
对象,使TObjectList
带有悬空的TTestObj
指针(但是您不会因此而崩溃,因为您不会以任何方式访问存储的TTestObj
对象,甚至由于TObjectList
而不会在OwnsObjects=false
析构函数中访问)。
您需要更改TTestList
来容纳IShared<TTestObj>
元素而不是TTestObj
元素(在这种情况下,您应该使用TList<T>
而不是TObjectList<T>
),并删除()
上的发票IShared
接口:
program TestSmartPointer;
{$APPTYPE CONSOLE}
uses
Spring,
Diagnostics,
Classes,
SysUtils,
System.Generics.Collections;
type
TTestObj = class
private
FDescription: string;
public
property Description: string read FDescription write FDescription;
destructor Destroy; override;
end;
TTestList = class(TObjectList<IShared<TTestObj>>)
destructor Destroy; override;
end;
var
LISTITEMCOUNT: integer;
LISTCOUNT: integer;
procedure Test_SmartPointer;
begin
Writeln('SmartPointer test started');
var lTestList := Shared.Make(TTestList.Create);
for var i := 1 to 10 do
begin
var lTestObj := Shared.Make(TTestObj.Create);
lTestObj.Description := i.ToString;
Writeln('TestObj added to Testlist with description ' + lTestObj.Description);
lTestList.Add(lTestObj);
end;
Writeln('SmartPointer test finished');
end;
{ TTestObj }
destructor TTestObj.Destroy;
begin
Writeln(Format('TTestObj with description %s is destroyed', [FDescription]));
inherited;
end;
{ TTestList }
destructor TTestList.Destroy;
begin
Writeln('TTestList is destroyed');
inherited;
end;
begin
Test_SmartPointer;
Readln;
end.
这里是有效的代码(感谢Remy Lebeau)。由于Delphi没有垃圾收集器,并且ARC已删除,因此我一直在寻找一种自动释放对象的常规结构。我对smartpointers的印象是,它有点复杂,无法用作自动释放对象的易于使用的通用结构。
program TestSmartPointer;
{$APPTYPE CONSOLE}
uses
Spring,
Diagnostics,
Classes,
SysUtils,
System.Generics.Collections;
type
TTestObj = class
private
FDescription: string;
public
property Description: string read FDescription write FDescription;
destructor Destroy; override;
end;
TTestList = class(TList<IShared<TTestObj>>)
public
destructor Destroy; override;
end;
procedure Test_SmartPointer;
var
lTestList: IShared<TTestList>;
lTestObj: IShared<TTestObj>;
i: integer;
begin
Writeln('SmartPointer test started');
lTestList := Shared.Make(TTestList.Create);
for i := 1 to 10 do
begin
lTestObj := Shared.Make(TTestObj.Create);
lTestObj.Description := i.ToString;
Writeln(format('TestObj with description %s added to Testlist', [lTestObj.Description]));
lTestList.Add(lTestObj);
end;
for lTestObj in lTestList do
begin
writeln(lTestObj.Description);
end;
Writeln('SmartPointer test finished');
end;
{ TTestObj }
destructor TTestObj.Destroy;
begin
Writeln(format('TestObj with description %s is destroyed', [FDescription]));
inherited;
end;
{ TTestList }
destructor TTestList.Destroy;
begin
Writeln('TTestList is destroyed');
inherited;
end;
begin
Test_SmartPointer;
Readln;
end.