1

Suppose I have following codes

Unit TalkerIntf.pas

unit TalkerIntf;

interface

{$MODE OBJFPC}

type

    ITalker = interface
        ['{95E1FAE3-7495-4404-AE88-6A7DB88383EC}']
        procedure say() ;
    end;

implementation
end.

Unit TalkerImpl.pas

unit TalkerImpl;

interface

{$MODE OBJFPC}

uses

    TalkerIntf;

type

    TTalker = class(TInterfacedObject, ITalker)
    public
        procedure say();
    end;

implementation

    procedure TTalker.say();
    begin
        writeln('Hello');
    end;

end.

Unit DelegateTalkerImpl.pas

unit DelegateTalkerImpl;

interface

{$MODE OBJFPC}

uses

    TalkerIntf;

type

    TDelegateTalker = class(TInterfacedObject, ITalker)
    private
        fActualTalker : ITalker;
    public
        constructor create(const talker : ITalker);
        destructor destroy(); override;

        property talker : ITalker read fActualTalker implements ITalker;
    end;

implementation

    constructor TDelegateTalker.create(const talker : ITalker);
    begin
        fActualTalker := talker;
    end;

    destructor TDelegateTalker.destroy();
    begin
        fActualTalker := nil;
        inherited destroy();
    end;

end.

and program memleak.pas

program memleak;

{$MODE OBJFPC}

uses

    TalkerIntf,
    TalkerImpl,
    DelegateTalkerImpl;

var
    talker : ITalker;

begin
    talker := TDelegateTalker.create(TTalker.create());
    talker.say();
end.

Compiling with FreePascal 3.0.4 and heaptrc on (-gh), heaptrc reports there are memory leak

$ fpc -gh memleak.pas
$ ./memleak

Heaptrc output

Hello
Heap dump by heaptrc unit
2 memory blocks allocated : 64/64
0 memory blocks freed     : 0/0
2 unfreed memory blocks : 64
True heap size : 32768
True free heap : 32384
Should be : 32448
Call trace for block $00007FA0D7846180 size 32
$000000000040020F
Call trace for block $00007FA0D78460C0 size 32

Why is this interface delegation causing memory leak? How to avoid it?

Original post

Update

It seems the only workaround is remove implements and do delegation manually. Following code does not suffer from memory leak.

unit DelegateTalkerImpl;

interface

{$MODE OBJFPC}

uses

    TalkerIntf;

type

    TDelegateTalker = class(TInterfacedObject, ITalker)
    private
        fActualTalker : ITalker;
    public
        constructor create(const talker : ITalker);
        destructor destroy(); override;

        procedure say();
    end;

implementation

    constructor TDelegateTalker.create(const talker : ITalker);
    begin
        fActualTalker := talker;
    end;

    destructor TDelegateTalker.destroy();
    begin
        fActualTalker := nil;
        inherited destroy();
    end;

    procedure TDelegateTalker.say();
    begin
        fActualTalker.say();
    end;
end.
Zamrony P. Juhara
  • 5,222
  • 2
  • 24
  • 40
  • I think that this is a case of fpc copying a long standing Delphi bug which it seems will never ever be fixed. https://stackoverflow.com/questions/31028266/ https://stackoverflow.com/questions/7640841/ https://stackoverflow.com/questions/22846335/ – David Heffernan Aug 29 '19 at 10:45

1 Answers1

1

According to ASerge's comment

When you define a variable as ITalker, the newly created object is not returned, but only the field that implements it. As a result, a newly created object is leaked. Delphi's behavior is the same.

To avoid memory leak but still using implements keyword, we need to assign to temporary variable of type other than ITalker and then typecast it to ITalker.

var
    talker : ITalker;
    delegateTalker : IInterface;

begin
    delegateTalker := TDelegateTalker.create(TTalker.create());
    talker := delegateTalker as ITalker;
    talker.say();
end.
Zamrony P. Juhara
  • 5,222
  • 2
  • 24
  • 40