[fpc-pascal] Delegate Interface class does not seem to be referenced counted [Solved]
Tony Whyman
tony.whyman at mccallumwhyman.com
Wed Aug 17 18:03:54 CEST 2016
I think that I have now found why the test program below (originally
posted a couple of weeks ago) did not work. It seems that when an
interface is delegated, the compiler may take a reference directly on
the delegated part of the interface and not to the object doing the
delegation.
In my original post, the main procedure was:
procedure TDelegateTest.DoRun;
var Intf: IMyInterface;
Intf2: IMyInterface;
begin
Intf := TMyClass.Create(TDelegateClass.Create);
Intf2 := TDelegateClass.Create;
...
and the output showed that TMyClass was not being automatically
destroyed. Changing the local variables declaration to:
procedure TDelegateTest.DoRun;
var Intf: IUnknown;
Intf2: IMyInterface;
results in this output:
Creating TDelegateClass
Creating TMyClass
Creating TDelegateClass
Destroying TMyClass
Destroying TDelegateClass
That is TMyClass is now being automatically destroyed, while the
TDelegateClass used to delegate the interface is not.
Changing the class definition to:
TMyClass = class(TInterfacedObject, IMyInterface)
private
FMyInterface: IMyInterface; // class type
property MyInterface: IMyInterface
read FMyInterface implements IMyInterface;
public
constructor Create(obj: TDelegateClass);
destructor Destroy; override;
end;
gives the output:
Creating TDelegateClass
Creating TMyClass
Creating TDelegateClass
Destroying TMyClass
Destroying TDelegateClass
Destroying TDelegateClass
which is really what I wanted. On the other hand, if I change the
procedure header back to:
procedure TDelegateTest.DoRun;
var Intf: IMyInterface;
Intf2: IMyInterface;
begin
the output is now back to:
Creating TDelegateClass
Creating TMyClass
Creating TDelegateClass
Destroying TDelegateClass
It seems that the main problem I had is due to the way the compiler
chooses which interface to reference count when you get an interface
from an object that has a delegated interface. If the type of the left
hand side of the assignment is that of the delegated interface, it
simply extracts that interface and only references it. On the other
hand, if it is any other compatible interface type (even IUnknown) then
the interface reference is to the object on the right hand side of the
assignment rather than to the delegated object.
This is probably a feature rather than a bug, but is certainly
confusing. It took me some time to get my head around it. The worst
thing is that I took the example from the FPC Documentation. The FPC
documentation on reference counting does not mention how it interacts
with delegated interfaces. It probably should.
Tony Whyman
MWA
On 10/08/16 13:42, Tony Whyman wrote:
> I'm using fpc 3.0.0 and trying to debug a program using COM
> interfaces. While reference counting seems to be working fine, there
> is one exception, that is when an interface is being used by
> delegation. In this case, the object doing the delegation does not
> seem to be reference counted. Is this a bug, a feature, or have I
> missed something?
>
> A simple test program follows. The output is:
>
> Creating TDelegateClass
> Creating TMyClass
> Creating TDelegateClass
> Destroying TDelegateClass
> Destroying TDelegateClass
>
> In the example, TMyClass is the interface class doing the delegation
> and while TDelegateClass is being destroyed when it goes out of scope,
> TMyClass is not.
>
> Tony Whyman
>
> MWA
>
> program project1;
>
> {$mode objfpc}{$H+}
>
> uses
> {$IFDEF UNIX}{$IFDEF UseCThreads}
> cthreads,
> {$ENDIF}{$ENDIF}
> Classes, SysUtils, CustApp
> { you can add units after this };
>
> type
>
> { TDelegateTest }
>
> TDelegateTest = class(TCustomApplication)
> protected
> procedure DoRun; override;
> public
> constructor Create(TheOwner: TComponent); override;
> destructor Destroy; override;
> procedure WriteHelp; virtual;
> end;
>
> IMyInterface = interface
> procedure P1;
> end;
>
> { TDelegateClass }
>
> TDelegateClass = class(TInterfacedObject, IMyInterface)
> private
> procedure P1;
> public
> constructor Create;
> destructor Destroy; override;
> end;
>
> { TMyClass }
>
> TMyClass = class(TInterfacedObject, IMyInterface)
> private
> FMyInterface: TDelegateClass; // class type
> property MyInterface: TDelegateClass
> read FMyInterface implements IMyInterface;
> public
> constructor Create(obj: TDelegateClass);
> destructor Destroy; override;
> end;
>
> { TDelegateClass }
>
> procedure TDelegateClass.P1;
> begin
> writeln('P1');
> end;
>
> constructor TDelegateClass.Create;
> begin
> inherited Create;
> writeln('Creating ',ClassName);
> end;
>
> destructor TDelegateClass.Destroy;
> begin
> writeln('Destroying ',ClassName);
> inherited Destroy;
> end;
>
> { TMyClass }
>
> constructor TMyClass.Create(obj: TDelegateClass);
> begin
> inherited Create;
> FMyInterface := obj;
> writeln('Creating ',ClassName);
> end;
>
> destructor TMyClass.Destroy;
> begin
> writeln('Destroying ',ClassName);
> inherited Destroy;
> end;
>
> { TDelegateTest }
>
> procedure TDelegateTest.DoRun;
> var Intf: IMyInterface;
> Intf2: IMyInterface;
> begin
> Intf := TMyClass.Create(TDelegateClass.Create);
> Intf2 := TDelegateClass.Create;
> // stop program loop
> Terminate;
> end;
>
> constructor TDelegateTest.Create(TheOwner: TComponent);
> begin
> inherited Create(TheOwner);
> StopOnException := True;
> end;
>
> destructor TDelegateTest.Destroy;
> begin
> inherited Destroy;
> end;
>
> procedure TDelegateTest.WriteHelp;
> begin
> { add your help code here }
> writeln('Usage: ', ExeName, ' -h');
> end;
>
> var
> Application: TDelegateTest;
> begin
> Application := TDelegateTest.Create(nil);
> Application.Title := 'Interface Delegation Test';
> Application.Run;
> Application.Free;
> end.
>
> _______________________________________________
> fpc-pascal maillist - fpc-pascal at lists.freepascal.org
> http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
More information about the fpc-pascal
mailing list