使用带有泛型的对象列表的内存溢出
memory overflow using the object list with generics
第一步:
使用代码编写应用程序:
unit Unit1;
interface
uses
System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants, System.Generics.Collections,
FMX.Types, FMX.Graphics, FMX.Controls, FMX.Forms, FMX.Dialogs, FMX.StdCtrls;
type
TObjChild = class;
TObjTest = class
private
FName: string;
FChilds: TList<TObjChild>;
public
property Name: string read FName write FName;
property Childs: TList<TObjChild> read FChilds write FChilds;
constructor Create;
destructor Destroy; override;
end;
TObjChild = class
private
FAdress: string;
FPostalCode: string;
public
property Adress: string read FAdress write FAdress;
property PostalCode: string read FPostalCode write FPostalCode;
end;
TForm1 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.fmx}
{ TObjTeste }
constructor TObjTest.Create;
begin
FChilds := TObjectList<TObjChild>.Create;
end;
destructor TObjTest.Destroy;
var
i: integer;
begin
for i := 0 to FChilds.count -1 do
begin
FChilds[I].Free;
end;
FreeAndNil(FChilds);
inherited;
end;
procedure TForm1.Button1Click(Sender: TObject);
var
I: Integer;
ListObjs: TList<TObjTest>;
lObjTeste: TObjTest;
lObjChild: TObjChild;
J: Integer;
begin
ListObjs := TList<TObjTest>.Create;
for I := 0 to 5000 do
begin
lObjTeste := TObjTest.Create;
for J := 0 to 2000 do
begin
lObjChild := TObjChild.Create;
lObjTeste.FChilds.Add(lObjChild)
end;
ListObjs.Add(lObjTeste);
end;
if MessageDlg('Delete objects?', TMsgDlgType.mtConfirmation, [TMsgDlgBtn.mbOK], 0) = idOK then
begin
for I := 0 To ListObjs.Count - 1
begin
ListObjs[I].Free;
end;
FreeAndNil(ListObjs);
end;
end;
end.
第 2 步:运行 应用程序并按下按钮 1
按下OK按钮后应用程序的messagedlg没有释放内存
第 3 步:有时应用程序重复步骤 returns 内存不足
问题出在这里:
constructor TObjTest.Create;
begin
FChilds := TObjectList<TObjChild>.Create;
end;
destructor TObjTest.Destroy;
var
i: integer;
begin
for i := 0 to FChilds.count - 1 do
begin
FChilds[i].Free;
end;
FreeAndNil(FChilds);
inherited;
end;
默认情况下 TObjectList<T>
拥有其成员的所有权。所以你不需要,也不应该,在析构函数中释放成员。
所以在这里:
for i := 0 to FChilds.count - 1 do
begin
FChilds[i].Free;
end;
你释放了成员。但是然后在这里:
FreeAndNil(FChilds);
对象列表也释放成员。谁已经被释放了。双重释放会导致您的运行时错误。
修复是删除对象列表成员的显式释放并依赖列表来完成工作:
destructor TObjTest.Destroy;
begin
FChilds.Free;
inherited;
end;
其成员的所有权是 TObjectList<T>
存在的唯一原因。这是除 TList<T>
提供的功能之外它提供的唯一功能。在这里阅读:http://docwiki.embarcadero.com/Libraries/en/System.Generics.Collections.TObjectList
最后,child的复数是children。
第一步: 使用代码编写应用程序:
unit Unit1;
interface
uses
System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants, System.Generics.Collections,
FMX.Types, FMX.Graphics, FMX.Controls, FMX.Forms, FMX.Dialogs, FMX.StdCtrls;
type
TObjChild = class;
TObjTest = class
private
FName: string;
FChilds: TList<TObjChild>;
public
property Name: string read FName write FName;
property Childs: TList<TObjChild> read FChilds write FChilds;
constructor Create;
destructor Destroy; override;
end;
TObjChild = class
private
FAdress: string;
FPostalCode: string;
public
property Adress: string read FAdress write FAdress;
property PostalCode: string read FPostalCode write FPostalCode;
end;
TForm1 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.fmx}
{ TObjTeste }
constructor TObjTest.Create;
begin
FChilds := TObjectList<TObjChild>.Create;
end;
destructor TObjTest.Destroy;
var
i: integer;
begin
for i := 0 to FChilds.count -1 do
begin
FChilds[I].Free;
end;
FreeAndNil(FChilds);
inherited;
end;
procedure TForm1.Button1Click(Sender: TObject);
var
I: Integer;
ListObjs: TList<TObjTest>;
lObjTeste: TObjTest;
lObjChild: TObjChild;
J: Integer;
begin
ListObjs := TList<TObjTest>.Create;
for I := 0 to 5000 do
begin
lObjTeste := TObjTest.Create;
for J := 0 to 2000 do
begin
lObjChild := TObjChild.Create;
lObjTeste.FChilds.Add(lObjChild)
end;
ListObjs.Add(lObjTeste);
end;
if MessageDlg('Delete objects?', TMsgDlgType.mtConfirmation, [TMsgDlgBtn.mbOK], 0) = idOK then
begin
for I := 0 To ListObjs.Count - 1
begin
ListObjs[I].Free;
end;
FreeAndNil(ListObjs);
end;
end;
end.
第 2 步:运行 应用程序并按下按钮 1
按下OK按钮后应用程序的messagedlg没有释放内存
第 3 步:有时应用程序重复步骤 returns 内存不足
问题出在这里:
constructor TObjTest.Create;
begin
FChilds := TObjectList<TObjChild>.Create;
end;
destructor TObjTest.Destroy;
var
i: integer;
begin
for i := 0 to FChilds.count - 1 do
begin
FChilds[i].Free;
end;
FreeAndNil(FChilds);
inherited;
end;
默认情况下 TObjectList<T>
拥有其成员的所有权。所以你不需要,也不应该,在析构函数中释放成员。
所以在这里:
for i := 0 to FChilds.count - 1 do
begin
FChilds[i].Free;
end;
你释放了成员。但是然后在这里:
FreeAndNil(FChilds);
对象列表也释放成员。谁已经被释放了。双重释放会导致您的运行时错误。
修复是删除对象列表成员的显式释放并依赖列表来完成工作:
destructor TObjTest.Destroy;
begin
FChilds.Free;
inherited;
end;
其成员的所有权是 TObjectList<T>
存在的唯一原因。这是除 TList<T>
提供的功能之外它提供的唯一功能。在这里阅读:http://docwiki.embarcadero.com/Libraries/en/System.Generics.Collections.TObjectList
最后,child的复数是children。