Spring4D中的TForm管理
TForm management in Spring4D
我有以下代码:
Project.dpr
program Project2;
uses
madExcept,
madLinkDisAsm,
madListHardware,
madListProcesses,
madListModules,
Spring.Container,
Vcl.Forms,
uRegistrations in '..\Memory leak II\uRegistrations.pas',
Unit3 in 'Unit3.pas' {MainForm},
Unit4 in 'Unit4.pas' {SecondaryForm},
Unit5 in 'Unit5.pas';
{$R *.res}
begin
RegisterTypes(GlobalContainer);
Application.Initialize;
Application.MainFormOnTaskbar := True;
// MainForm:=TMainForm.Create(nil);
Application.CreateForm(TMainForm, MainForm);
MainForm.SecondaryForm := Globalcontainer.Resolve<ISecondaryForm>;
Application.Run;
end.
uRegistrations.pas 注册接口
unit uRegistrations;
interface
uses
Spring.Container;
procedure RegisterTypes(Container: TContainer);
implementation
uses
Unit5,
Unit4;
procedure RegisterTypes(Container: TContainer);
begin
container.RegisterType<ISecondaryForm, TSecondaryForm>.DelegateTo(
function: TSecondaryForm
begin
result := TSecondaryForm.Create(nil);
end);
Container.Build;
end;
end.
Unit3.pas持主形态
unit Unit3;
interface
uses
Winapi.Windows,
Winapi.Messages,
System.SysUtils,
System.Variants,
System.Classes,
Vcl.Graphics,
Unit5,
Vcl.Controls,
Vcl.Forms,
Vcl.Dialogs;
type
TMainForm = class(TForm)
private
{ Private declarations }
FSecondaryForm: ISecondaryForm;
public
{ Public declarations }
property SecondaryForm: ISecondaryForm read FSecondaryForm write FSecondaryForm;
end;
var
MainForm: TMainForm;
implementation
{$R *.dfm}
end.
Unit4.pas 次要形式
unit Unit4;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
unit5,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs;
type
TSecondaryForm = class(TForm, ISecondaryForm)
private
{ Private declarations }
public
{ Public declarations }
end;
//var
// SecondaryForm: TSecondaryForm;
implementation
{$R *.dfm}
end.
最后 Unit5.pas 接口声明
{$M+}
unit Unit5;
interface
type
ISecondaryForm=interface
['{62D63E9A-A3AD-435B-8938-9528E70D78B1}']
end;
implementation
end.
它可以正常编译和运行,但是当我关闭应用程序时,出现了三个内存泄漏。
allocation number: 8482 program up time: 721 ms type: Brush Handle
handle: 1027f5 style: BS_SOLID color: $f0f0f0
allocation number: 8318 program up time: 697 ms type: TSecondaryForm
address: $d51ac64 size: 924 access rights: read/write
allocation number: 8267 program up time: 693 ms type: Font Handle
handle: d0a28f1 face: Tahoma height: -11
为什么会发生这种情况,我该如何解决?
编辑
回答后,我实施了以下解决方案(评论突出了我得到的错误:
procedure RegisterTypes(Container: TContainer);
begin
container.RegisterType<ISecondaryForm, TSecondaryForm>.DelegateTo(
function: TSecondaryForm
begin
result := TSecondaryForm.Create(nil);
result.Owner:=Application.MainForm;//cannot assign to a read-only property
result.Parent:=Application; //incompatible types
result.Parent:=application.MainForm;//memory leak
end);
Container.Build;
end;
我也试过修改TSecondaryForm的OnClose方法如下:
procedure TSecondaryForm.FormClose(Sender: TObject; var Action: TCloseAction);
begin
Action:=caFree; //memory leak
end;
但是我遇到了内存泄漏问题。
以上所有技巧我做错了什么?
最后我只是按照评论中的建议使用 _AddRef 和 _Release 这两种方法来管理引用计数,我没有更多内存泄漏。
TSecondaryForm = class(TForm, ISecondaryForm)
private
{ Private declarations }
protected
FRefCount: Integer;
function _AddRef: Integer; stdcall;
function _Release: Integer; stdcall;
public
{ Public declarations }
end;
function TSecondaryForm._AddRef: Integer;
begin
Result := InterlockedIncrement(FRefCount);
end;
function TSecondaryForm._Release: Integer;
begin
Result := InterlockedDecrement(FRefCount);
if Result=0 then
self.Free;
end
TComponent
后代(如 TForm
)禁用接口引用计数,因此没有人释放次要形式。内存模型是基于所有者的,也就是说,当拥有一个对象的父对象被释放时,它会释放它的所有子对象。
因此,您可以将所有者传递给工厂函数上的表单(可能 Application
,或 Application.MainForm
)并遵守 TComponent 的内存模型,或者在 OnClose
事件并将 Action
设置为 caFree
。前者会在应用关闭时销毁表单,后者会在次级表单关闭时立即销毁(尽快)
如果您希望通过接口引用计数来处理表单(或任何继承自 TComponent 的 class),那么您需要自己实现它(查看 System.TInterfacedObject
作为如何实现的示例去做)。
您基本上需要将 IInterface
重新实现到要启用引用计数的 class:
type
TInterfacedForm = class(TForm, IInterface)
// look at System.TInterfacedObject
end;
如果您这样做,请记住它不应由所有者机制处理。如果您将它注册到容器并使用其默认创建机制,它将在 Spring4D 1.2 中将 nil 传递给所有者 - 请参阅 Spring.Container.Resolvers.TComponentOwnerResolver
)。在您需要在 DelegateTo
.
中使用 nil 显式创建它之前的任何版本
如果您正在处理通过其父级放置到其他控件(如框架)上的接口的任何控件 属性 还请记住,在这种情况下,另一种内存管理机制会发挥作用,这可能会破坏这样一个组件,如果它的父组件被破坏了——如果你只是处理接口表单,这不是问题,但我想我在这里提到它是为了完整性。
我有以下代码:
Project.dpr
program Project2;
uses
madExcept,
madLinkDisAsm,
madListHardware,
madListProcesses,
madListModules,
Spring.Container,
Vcl.Forms,
uRegistrations in '..\Memory leak II\uRegistrations.pas',
Unit3 in 'Unit3.pas' {MainForm},
Unit4 in 'Unit4.pas' {SecondaryForm},
Unit5 in 'Unit5.pas';
{$R *.res}
begin
RegisterTypes(GlobalContainer);
Application.Initialize;
Application.MainFormOnTaskbar := True;
// MainForm:=TMainForm.Create(nil);
Application.CreateForm(TMainForm, MainForm);
MainForm.SecondaryForm := Globalcontainer.Resolve<ISecondaryForm>;
Application.Run;
end.
uRegistrations.pas 注册接口
unit uRegistrations;
interface
uses
Spring.Container;
procedure RegisterTypes(Container: TContainer);
implementation
uses
Unit5,
Unit4;
procedure RegisterTypes(Container: TContainer);
begin
container.RegisterType<ISecondaryForm, TSecondaryForm>.DelegateTo(
function: TSecondaryForm
begin
result := TSecondaryForm.Create(nil);
end);
Container.Build;
end;
end.
Unit3.pas持主形态
unit Unit3;
interface
uses
Winapi.Windows,
Winapi.Messages,
System.SysUtils,
System.Variants,
System.Classes,
Vcl.Graphics,
Unit5,
Vcl.Controls,
Vcl.Forms,
Vcl.Dialogs;
type
TMainForm = class(TForm)
private
{ Private declarations }
FSecondaryForm: ISecondaryForm;
public
{ Public declarations }
property SecondaryForm: ISecondaryForm read FSecondaryForm write FSecondaryForm;
end;
var
MainForm: TMainForm;
implementation
{$R *.dfm}
end.
Unit4.pas 次要形式
unit Unit4;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
unit5,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs;
type
TSecondaryForm = class(TForm, ISecondaryForm)
private
{ Private declarations }
public
{ Public declarations }
end;
//var
// SecondaryForm: TSecondaryForm;
implementation
{$R *.dfm}
end.
最后 Unit5.pas 接口声明
{$M+}
unit Unit5;
interface
type
ISecondaryForm=interface
['{62D63E9A-A3AD-435B-8938-9528E70D78B1}']
end;
implementation
end.
它可以正常编译和运行,但是当我关闭应用程序时,出现了三个内存泄漏。
allocation number: 8482 program up time: 721 ms type: Brush Handle handle: 1027f5 style: BS_SOLID color: $f0f0f0
allocation number: 8318 program up time: 697 ms type: TSecondaryForm address: $d51ac64 size: 924 access rights: read/write
allocation number: 8267 program up time: 693 ms type: Font Handle handle: d0a28f1 face: Tahoma height: -11
为什么会发生这种情况,我该如何解决?
编辑
回答后,我实施了以下解决方案(评论突出了我得到的错误:
procedure RegisterTypes(Container: TContainer);
begin
container.RegisterType<ISecondaryForm, TSecondaryForm>.DelegateTo(
function: TSecondaryForm
begin
result := TSecondaryForm.Create(nil);
result.Owner:=Application.MainForm;//cannot assign to a read-only property
result.Parent:=Application; //incompatible types
result.Parent:=application.MainForm;//memory leak
end);
Container.Build;
end;
我也试过修改TSecondaryForm的OnClose方法如下:
procedure TSecondaryForm.FormClose(Sender: TObject; var Action: TCloseAction);
begin
Action:=caFree; //memory leak
end;
但是我遇到了内存泄漏问题。
以上所有技巧我做错了什么?
最后我只是按照评论中的建议使用 _AddRef 和 _Release 这两种方法来管理引用计数,我没有更多内存泄漏。
TSecondaryForm = class(TForm, ISecondaryForm)
private
{ Private declarations }
protected
FRefCount: Integer;
function _AddRef: Integer; stdcall;
function _Release: Integer; stdcall;
public
{ Public declarations }
end;
function TSecondaryForm._AddRef: Integer;
begin
Result := InterlockedIncrement(FRefCount);
end;
function TSecondaryForm._Release: Integer;
begin
Result := InterlockedDecrement(FRefCount);
if Result=0 then
self.Free;
end
TComponent
后代(如 TForm
)禁用接口引用计数,因此没有人释放次要形式。内存模型是基于所有者的,也就是说,当拥有一个对象的父对象被释放时,它会释放它的所有子对象。
因此,您可以将所有者传递给工厂函数上的表单(可能 Application
,或 Application.MainForm
)并遵守 TComponent 的内存模型,或者在 OnClose
事件并将 Action
设置为 caFree
。前者会在应用关闭时销毁表单,后者会在次级表单关闭时立即销毁(尽快)
如果您希望通过接口引用计数来处理表单(或任何继承自 TComponent 的 class),那么您需要自己实现它(查看 System.TInterfacedObject
作为如何实现的示例去做)。
您基本上需要将 IInterface
重新实现到要启用引用计数的 class:
type
TInterfacedForm = class(TForm, IInterface)
// look at System.TInterfacedObject
end;
如果您这样做,请记住它不应由所有者机制处理。如果您将它注册到容器并使用其默认创建机制,它将在 Spring4D 1.2 中将 nil 传递给所有者 - 请参阅 Spring.Container.Resolvers.TComponentOwnerResolver
)。在您需要在 DelegateTo
.
如果您正在处理通过其父级放置到其他控件(如框架)上的接口的任何控件 属性 还请记住,在这种情况下,另一种内存管理机制会发挥作用,这可能会破坏这样一个组件,如果它的父组件被破坏了——如果你只是处理接口表单,这不是问题,但我想我在这里提到它是为了完整性。