Interface delegation has a few details that need to be done right to avoid memory leaks.
First of the class instance the interface is delegated to needs to forward it's IInterface implementation to the container class (which is what
TAggregatedObject does).
Then you should not store the contained class as an interface, but instead as a class instance to avoid a circular reference.
I'll use the example by
ASerge as it's in one unit:
{$IFDEF FPC}{$MODE OBJFPC}{$ENDIF}
{$APPTYPE CONSOLE}
type
ITalker = interface(IInterface)
['{C6512C47-A2ED-40E7-A3AE-C74EF5E71071}']
procedure Say;
end;
TTalker = class(TAggregatedObject, ITalker)
public
procedure Say;
end;
TDelegateTalker = class(TInterfacedObject, ITalker)
strict private
FActualTalker: TTalker;
function GetTalker: ITalker;
public
constructor Create;
destructor Destroy; override;
property Talker: ITalker read GetTalker implements ITalker;
end;
constructor TDelegateTalker.Create;
begin
inherited Create;
FActualTalker := TTalker.Create(Self);
end;
destructor TDelegateTalker.Destroy;
begin
FActualTalker.Free;
inherited;
end;
function TDelegateTalker.GetTalker: ITalker;
begin
Result := FActualTalker;
end;
procedure TTalker.Say;
begin
Writeln('TTalker.Say');
end;
{-$DEFINE NOLEAK}
procedure Test;
var
DelegateTalker: {$IFDEF NOLEAK}IInterface{$ELSE}ITalker{$ENDIF};
Talker: ITalker;
begin
DelegateTalker := TDelegateTalker.Create {$IFNDEF NOLEAK}as ITalker{$ENDIF};
Talker := (DelegateTalker as ITalker);
Talker.Say;
end;
begin
{$IFDEF DCC} ReportMemoryLeaksOnShutdown := True; {$ENDIF}
Test;
Readln;
end.
In this case it doesn't matter whether
NOLEAK is defined or not (though for some reason the compiler barfs if there is no
as ITalker in case of
NOLEAK; I'll have to take a look at that).
It's important to note that interface delegation and interface injection don't work well together except one
really knows what one is doing (and even then it can be frickle), so one should decide for one or the other.