Sentient
Sentient

Reputation: 1112

Freeing interfaced object that descends from a TRectangle

I'm new to interfaces and have been trying them out in my latest project. I have this (simplified) interface:

IBoardShape = interface(IInterface)
  function GetColor: integer; 
  procedure SetColor(const aColor: integer);
  property Color: integer read GetColor write SetColor;
end;

Several classes descend from it like so:

TGameRectangle = class(TRectangle, IBoardShape)
private
  FColor: integer;
  function GetColor: integer;
  procedure SetColor(const aColor: integer);
  property Color: integer read GetColor write SetColor;
end;

I have a factory for creating the shapes in its own data module.

function TdmShapeManager.CreateRect(aParent: TLayout): IBoardShape;
var
  lRect: TGameRectangle;
begin
  lRect := TGameRectangle.Create(self);
  lRect.Parent := aParent; 
  lRect.Align := TAlignLayout.alClient;
  result := lRect as IBoardShape;
end;

The result is added to a TList<IBoardShape>.

All of this worked well, until I started trying to remove shapes at run time. I found that TList[I] := nil didn't free the item, the control would just stay on the screen. So, from here I'm not sure to do. I found I can cast the object to a TShape and call .Free on it, but that doesn't sound right. (I tried it and it works but then it leads to other problems - errors inside Delphi code when trying to free an interface.)

One thing that I'm not sure about: Since my TGameRectangle doesn't descend from TInterfacedObject, should I be doing my own reference counting? Or am I misunderstanding what TInterfacedObject is for?

Upvotes: 2

Views: 166

Answers (2)

David Heffernan
David Heffernan

Reputation: 612794

I'm going to restrict my answer to desktop platforms which do not have ARC. That's because on ARC platforms there is nothing to do. The ARC framework will manage lifetime for you and you must not re-implement _AddRef and _Release.

For non-ARC platforms you have a fundamental design problem. You want your object's lifetime to be controlled by two independent mechanisms:

  1. The TComponent owner, and
  2. The interface reference count.

You need to make up your mind to do it one way or the other. But not both.

If you opt for the first option you just need to make sure that you clear the interface reference before the owner is destroyed. Then let the owner destroy your rectangle object.

If you opt for the second option you need to pass nil as the owner parameter of the constructor. And then implement interface reference counted lifetime management as per TInterfacedObject.

A common pattern is to let the value of the owner passed to the constructor determine the lifetime model. So, when the owner is nil, interface reference counting controls the life. Otherwise the owner controls it. The canonical example of this pattern is TXMLDocument.

All that said, I don't think this pattern works at well with visual components. The framework is designed so that visual component lifetime is not controlled by interface reference counting. I think it is folly to go against that design. I suggest you opt for option 1.

Upvotes: 1

Mason Wheeler
Mason Wheeler

Reputation: 84540

Yes, if you want to use interfaces and reference counting, you need to either inherit from a type that provides reference counting or provide your own implementations of _AddRef and _Release.

What do the _AddRef and _Release that your TGameRectangle has do?

Upvotes: 0

Related Questions