Izuel
Izuel

Reputation: 452

Freeing Interfaced object

I got a doubt about freeing an Interfaced object.

My interface looks like this:

type IBase = interface(IInterface)
    function test: Boolean;  
end;

This other class have some attributes that i wanted in all the interfaced classes:

type TBase = class(TInterfacedObject)
    protected
        FQuery: TADOQuery;
        FADOConnection: TADOConnection;
    public
        constructor Create; virtual; abstract;
        destructor Destroy;
    end;

I got some clases that inherits from the previous class and also implements the interface.

type TExample= class(TBase, IBase)
    public
        function test: Boolean;
        destructor Destroy;
end;

So, using this schema I'm able to use the classes like this:

procedure someProcedure(aux : IBase);
begin
    aux.test; //Aux is an instance of TExample and I'm using the interfaced method
end;

My question is,How I destroy this aux object that is a IBase interfacedObject? I tried this things, checking first that is not a nil object:

(aux as TObject).Destroy; //Invalid Pointer operation
(aux as TInterfacedObject).Destroy; //Invalid Pointer operation
(aux as TExample).Destroy; Also invalid Pointer operation!!??

Instead of freeing the object, I read that because inherits from TInterfacedObject and implements the interface I should use this:

aux := nil;

And the reference counter will do the magic, but using ReportMemoryLeaksOnShutdown := True; in my project there are some leaks and the code never reach the breakpoint of the destructor.

Am I missing something??

EDIT:

I changed my constructors, now are like:

type TBase = class(TInterfacedObject)
    protected
        FQuery: TADOQuery;
        FADOConnection: TADOConnection;
    public
        constructor Create; 
        destructor Destroy; override;
    end;


type TExample= class(TBase, IBase)
    public
        function test: Boolean;
end;

Now i guess that make more sense since the TBase is allocating and freeing the objects and the TExample class inherits the destructor.

Upvotes: 4

Views: 3164

Answers (3)

fantaghirocco
fantaghirocco

Reputation: 4878

Your example is somehow strange.

Since you have declared the TBase constructor as virtual abstract, a constructor has to be declared in the TExample class.

As others have said, you have to add the override directive to your destructors.

program Project1;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  FastMM4,
  System.SysUtils, ADODB, ActiveX;

type
  IBase = interface(IInterface)
    function test: Boolean;
  end;

  TBase = class(TInterfacedObject)
    protected
      FQuery: TADOQuery;
      FADOConnection: TADOConnection;
    public
      constructor Create; virtual; abstract;
      destructor Destroy; override;
  end;

  TExample= class(TBase, IBase)
    public
      function test: Boolean;
      constructor Create; reintroduce; virtual;
      destructor Destroy; override;
  end;

{ TBase }

destructor TBase.Destroy;
begin
  FQuery.Free;
  FADOConnection.Free;
  inherited;
end;

{ TExample }

constructor TExample.Create;
begin
  //inherited;
  FADOConnection := TADOConnection.Create(nil);
  FQuery := TADOQuery.Create(nil);
end;

destructor TExample.Destroy;
begin
  inherited;
end;

function TExample.test: Boolean;
begin
  Result := False;
end;


var
  example: IBase;

begin
  CoInitialize(nil);

  example := TExample.Create;
  try
    WriteLn(example.test);
  finally
    example := nil;
  end;

  CoUninitialize;
end.

Another thing that looks weird to me is freeing the FADO objects in the base destructor since they're created in a derived class.


As a side note, I'd prefer a design like this:

type
  TBase = class(TInterfacedObject)
    protected
      FQuery: TADOQuery;
      FADOConnection: TADOConnection;
    public
      constructor Create;
      destructor Destroy; override;
  end;

  TExample= class(TBase, IBase)
    public
      function test: Boolean;
      constructor Create;
      destructor Destroy; override;
  end;

{ TBase }

constructor TBase.Create;
begin
  inherited;
  FADOConnection := TADOConnection.Create(nil);
  FQuery := TADOQuery.Create(nil);
end;

destructor TBase.Destroy;
begin
  FQuery.Free;
  FADOConnection.Free;
  inherited;
end;

{ TExample }

constructor TExample.Create;
begin
  inherited;
  . . .
end;

Upvotes: 1

Jens Borrisholt
Jens Borrisholt

Reputation: 6402

There are a few problems here:

First of all you need to add override to the destructor as pointed out in the comment. But the declaration of TBase is realy what is killing the whole thing:

You have an virtual empty constructor which hides the one from TObject. And besides from that when you have a TInterfacedObject you should work with the interface not the instance.

so

type TBase = class(TInterfacedObject)
    protected
        FQuery: TADOQuery;
        FADOConnection: TADOConnection;
    public
        constructor Create; virtual; abstract;
        destructor Destroy;
    end;

Should really be

  TBase = class(TInterfacedObject)
  protected
    FQuery: TADOQuery;
    FADOConnection: TADOConnection;
  public
    constructor Create; reintroduce; virtual;
    destructor Destroy; override;
  end;

NOTE reintroduce.

With this in hand, and the override added to the destructors you can free you object as you are use to :

procedure TForm1.FormCreate(Sender: TObject);
var
  aux: TExample;
begin
  aux := TExample.Create;
  aux.Free;    
end;

No need to cast it. Here you created an intance you self therefor you have to free it youself

But as I said before when you have a TInterfacedObject you should work with the interface and the object will free it self when it is not referenced any more.

So the example from before should realy have been like this:

procedure TForm1.FormCreate(Sender: TObject);
var
  aux: iBase;
begin
  aux := TExample.Create;
  //do stuff with aux 
end;

My complete test code looks like this :

type
  IBase = interface(IInterface)
    function test: Boolean;
  end;

  TBase = class(TInterfacedObject)
  protected
    FQuery: TADOQuery;
    FADOConnection: TADOConnection;
  public
    constructor Create; reintroduce; virtual;
    destructor Destroy; override;
  end;

  TExample = class(TBase, IBase)
  public
    function test: Boolean;
    destructor Destroy; override;
  end;

  { TBase }

constructor TBase.Create;
begin
  inherited;
end;

destructor TBase.Destroy;
begin
  inherited;
end;

{ TExample }

destructor TExample.Destroy;
begin
  test;
  inherited;
end;

function TExample.test: Boolean;
begin
  ShowMessage('');
end;

Then just call it:

procedure TForm1.FormCreate(Sender: TObject);
var
  aux: iBase;
begin
  aux := TExample.Create;
end;

Upvotes: -2

Wosi
Wosi

Reputation: 45343

Don't free interfaced objects manually!

When a class derives from TInterfacedObject then it is automatically reference counted and will be freed automatically as soon as there is no interface reference to it left.

What does it mean?

The following procedure references an instance of TExample over its interface IBase. It clears up memory automatically when the Obj variable is being removed from stack.

procedure Foo;
var
  Obj: IBase;
begin
  Obj := TExample.Create; // reference count will be set to 1 
  Obj.test;
end; // reference count will be set to 0 and Obj will be freed 

The next procedure references an instance of TExample over its class name. The reference counting is inactive here. The compiler includes no calls to _AddRef and _Release. So this procedure leaks memory:

procedure Foo;
var
  Obj: TExample;
begin
  Obj := TExample.Create; 
  Obj.test;
end; // Obj will not be freed automatically

So you would need to clean the heap on your own like this:

procedure Foo;
var
  Obj: TExample;
begin
  Obj := TExample.Create; 
  try
    Obj.test;        
  finally
    Obj.Free;
  end;
end; 

It works but it can be dangerous when Obj is passed around. As soon as a reference to the object is stored in an interface reference. Let's see this:

procedure Bar(Obj: IBase);
begin
  //...
end;

procedure Foo;
var
  Obj: TExample;
begin
  Obj := TExample.Create; 
  try
    Bar(Obj); 
    Obj.test; // Access violation!       
  finally
    Obj.Free;
  end;
end; 

What happens here?

Obj is created and stored as a class reference. The reference count is 0. When Bar(Obj) is called the object will be stored in an interface reference. The compiler includes calls to _AddRef and _Release when Bar is called. The reference counter will be increased and decreased, so it becomes 0 again and the object destroys itself.

How to deal with it?

  • Don't free interfaced objects manually!
  • Let the reference counting do the work for you.
  • Don't store derives of TInterfacedObject by its concrete class.
  • Store them only by the interfaces they implement.
  • Avoid circular references between interfaced objects. There is no garbage collector in Delphi!

Upvotes: 5

Related Questions