I am reading Hodges book "More Coding in Delphi", section on Factory Pattern.
Trying to learn stuff. Broke down my code into small unit.
I use ReportMemoryLeaksOnShutDown := True;
and fallowing code produces me a memory leak. Why does it occur and how do I fix it?
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.
This is a compiler defect. Defining an anonymous method in the initialization section of the unit appears to lead to that anonymous method not being finalized, and so leaked. In this case I would work around the issue by moving the code from the initialization section to the class constructor
.
So, remove the initialization
section completely, and change the class constructor to be like this:
class constructor TGatewayFactory.Create;
begin
FGatewayTypeAndFunctionList := TList<TGatewayTypeAndFunction>.Create;
AddGateway(
gtSwedbank,
function: TBaseGateway
begin
Result := TSwedbankGateway.Create;
end
);
end;
Here is the simplest reproduction that I can concoct:
unit Unit1;
interface
implementation
type
TProc = reference to procedure;
var
Foo: TProc;
initialization
ReportMemoryLeaksOnShutdown := True;
Foo := procedure begin end;
end.
When you include this unit in a project, the anonymous method is reported as being leaked.
But this variant does not report a leak:
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.
The defect was fixed in XE8.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With