JustMe
JustMe

Reputation: 2383

Passing object reference as an Interface

I'm passing created object to a constructor of another object which need an Interface which that object implements.

  ISomeInterface = interface
  ['{840D46BA-B9FB-4273-BF56-AD0BE40AA3F9}']
  end;

  TSomeObject = class(TInterfacedObject, ISomeinterface)
  end;

  TSomeObject2 = class
  private
    FSomeInterface: ISomeinterface;
  public
    constructor Create(SomeObject: ISomeInterface);
  end;

var
Form1: TForm1; // main form
SomeObject: TSomeObject;

constructor TSomeObject2.Create(SomeObject: ISomeInterface);
begin
  FSomeInterface := SomeObject;
end;

// main form creating
procedure TForm1.FormCreate(Sender: TObject);
var SomeObject2: TSomeObject2;
begin
  SomeObject := TSomeObject.Create;
  //  SomeObject2 := TSomeObject2.Create(nil);        // ok
  SomeObject2 := TSomeObject2.Create(SomeObject);     // not ok
  try
  // do some things
  finally
    SomeObject2.Free;
  end;
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  SomeObject.Free; // if passed to a SomeObject2 Constructor - freeing it causing av
end;

After I close main form it gives me an AV and a memory leak - whole main form has leaked. If I'm passing nil to a TSomeObject constructor everything is well. Is compilator freeing FSomeInterface by reference counting and I'm shouldn't try to free SomeObject in mainForm destructor? How can I avoid it?

Upvotes: 2

Views: 595

Answers (1)

Uwe Raabe
Uwe Raabe

Reputation: 47819

TSomeObject inherited from TInterfacedObject and thus is reference counted. Your instance of TSomeObject is not reference counted and should be removed or replaced by an interface variable.

If you need the instance of TSomeObject created in FormCreate, you should assign it to a variable of type ISomeInterface, so that the reference counting will work for that, too.

Another approach is to inherit from TInterfacedPersistant instead of TInterfacedObject to avoid the reference counting.

To explain what is happening in your code:

procedure TForm1.FormCreate(Sender: TObject);
var SomeObject2: TSomeObject2;
begin
  { Here you create the instance and assign it to a variable holding the instance.
    After this line the reference count of the instance is 0 }
  SomeObject := TSomeObject.Create;
  //  SomeObject2 := TSomeObject2.Create(nil);        // ok
  { Using the instance as a parameter will increase the reference count to 1 }
  SomeObject2 := TSomeObject2.Create(SomeObject);     // not ok
  try
  // do some things
  finally
    { Freeing SomeObject2 also destroys the interface reference FSomeInterface is
      pointing to (which is SomeObject), decreasing the reference count to 0, which
      in turn frees the instance of TSomeObject. }
    SomeObject2.Free;
  end;
  { Now, after SomeObject is freed, the variable points to invalid memory causing the
    AV in FormDestroy. }
end;

Upvotes: 7

Related Questions