【问题标题】:TForm management in Spring4DSpring4D中的TForm管理
【发布时间】:2016-07-18 21:43:54
【问题描述】:

我有以下代码:

项目.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.

它会定期编译和运行,但是当我关闭应用程序时,我发现了三个内存泄漏。

分配号:8482 程序运行时间:721 ms 类型:刷柄 手柄:$461027f5 样式:BS_SOLID 颜色:$f0f0f0

分配号:8318 程序运行时间:697 ms 类型:TSecondaryForm 地址:$d51ac64 大小:924 访问权限:读/写

分配号:8267 程序运行时间:693 ms 类型:字体句柄 手柄:$1d0a28f1 面:Tahoma 身高:-11

为什么会发生这种情况,我该如何解决?

编辑

得到答案后,我实现了以下解决方案(cmets突出显示我得到的错误:

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;

但我遇到了内存泄漏。

上述所有技术我做错了什么?

最后,我只是按照 cmets 中的建议使用了 _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

【问题讨论】:

标签: delphi spring4d


【解决方案1】:

如果您希望通过接口引用计数来处理表单(或任何继承自 TComponent 的类),那么您需要自己实现它(以 System.TInterfacedObject 为例说明如何实现)。

您基本上需要将IInterface 重新实现到要启用引用计数的类:

type
  TInterfacedForm = class(TForm, IInterface)
    // look at System.TInterfacedObject
  end;

如果您这样做,请记住它不应由所有者机制处理。如果您将它注册到容器并使用它的默认创建机制,它将从 Spring4D 1.2 开始将 nil 传递给所有者 - 请参阅Spring.Container.Resolvers.TComponentOwnerResolver)。在任何版本中,您都需要在 DelegateTo 中使用 nil 显式创建它。

如果您正在处理通过其父属性放置到其他控件(如框架)上的任何接口控件,请记住,在这种情况下,另一个内存管理机制会发挥作用,如果它的父组件可能会破坏这样的组件正在被破坏 - 如果您只是处理接口表单,这不是问题,但我想我在这里提到它是为了完整性。

【讨论】:

    【解决方案2】:

    TComponent 后代(如TForm)禁用接口引用计数,因此没有人释放辅助表单。内存模型是基于所有者的,也就是说,当拥有一个对象的父对象被释放时,它会释放它的所有子对象。

    因此,您可以通过工厂函数(可能是 ApplicationApplication.MainForm)将所有者传递给表单并遵守 TComponent 的内存模型或在表单的 OnClose 事件上添加挂钩并设置ActioncaFree。前者会在应用关闭时销毁表单,后者会在二级表单关闭后立即销毁(尽快)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-06
      • 1970-01-01
      • 2015-03-27
      • 2015-06-18
      相关资源
      最近更新 更多