工厂模式,内存泄漏

Factory pattern, memory leak

我正在阅读 Hodges 的书 "More Coding in Delphi",关于工厂模式的部分。 努力学习东西。将我的代码分解成小单元。 我使用 ReportMemoryLeaksOnShutDown := True; 并且休闲代码会导致内存泄漏。为什么会发生,我该如何解决?

unit Unit2;

interface

uses
  Generics.Collections, System.SysUtils;

type
  TGatewayTpe = (gtSwedbank, gtDNB);

type
  TBaseGateway = class

  end;

type
  TSwedbankGateway = class(TBaseGateway)
  end;

type
  TGatewayFunction = reference to function: TBaseGateway;

type
  TGatewayTypeAndFunction = record
    GatewayType: TGatewayTpe;
    GatewayFunction: TGatewayFunction;
  end;

type
  TGatewayFactory = class
  strict private
    class var FGatewayTypeAndFunctionList: TList<TGatewayTypeAndFunction>;
  public
    class constructor Create;
    class destructor Destroy;
    class procedure AddGateway(const AGatewayType: TGatewayTpe;
      const AGatewayFunction: TGatewayFunction);
  end;

implementation

class procedure TGatewayFactory.AddGateway(const AGatewayType: TGatewayTpe;
  const AGatewayFunction: TGatewayFunction);

var
  _GatewayTypeAndFunction: TGatewayTypeAndFunction;
begin
  _GatewayTypeAndFunction.GatewayType := AGatewayType;
  _GatewayTypeAndFunction.GatewayFunction := AGatewayFunction;

  FGatewayTypeAndFunctionList.Add(_GatewayTypeAndFunction);
end;

class constructor TGatewayFactory.Create;
begin
  FGatewayTypeAndFunctionList := TList<TGatewayTypeAndFunction>.Create;
end;

class destructor TGatewayFactory.Destroy;
begin
  FreeAndNil(FGatewayTypeAndFunctionList);
end;

initialization
  TGatewayFactory.AddGateway(
    gtSwedbank, 
    function: TBaseGateway
    begin
      Result := TSwedbankGateway.Create;
    end
  );

end.

这是一个编译器缺陷。在单元的初始化部分定义匿名方法似乎会导致该匿名方法未被最终确定,从而导致泄漏。在这种情况下,我会通过将代码从初始化部分移动到 class constructor.

来解决这个问题

所以,完全删除 initialization 部分,并将 class 构造函数更改为如下所示:

class constructor TGatewayFactory.Create;
begin
  FGatewayTypeAndFunctionList := TList<TGatewayTypeAndFunction>.Create;
  AddGateway(
    gtSwedbank,
      function: TBaseGateway
      begin
        Result := TSwedbankGateway.Create;
      end
  );
end;

这是我能编造的最简单的复制品:

unit Unit1;

interface

implementation

type
  TProc = reference to procedure;

var
  Foo: TProc;

initialization
  ReportMemoryLeaksOnShutdown := True;
  Foo := procedure begin end;

end.

当您将此单元包含在项目中时,匿名方法被报告为泄漏。

但是这个变体没有报告泄漏:

unit Unit1;

interface

implementation

type
  TProc = reference to procedure;

var
  Foo: TProc;

procedure DoInit;
begin
  Foo := procedure begin end;
end;

initialization
  ReportMemoryLeaksOnShutdown := True;
  DoInit;

end.

该缺陷已在 XE8 中修复。