Sentient
Sentient

Reputation: 1122

Freeing an interfaced object with ARC vs without - Delphi

I'm working on a project where I have a interfaced TRectangle like this:

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

TGameRectangle = class(TRectangle, IBoardShape)
private
  FColor: integer;
  procedure SetColor(const aColor: integer);
  function GetColor: integer;
  property Color: integer read GetColor write SetColor;
protected
  {$IFNDEF AUTOREFCOUNT}
  [Volatile] FRefCount: Integer;
  {$ENDIF}
  function _AddRef: Integer; stdcall;
  function _Release: Integer; stdcall;
end;

_AddRef and _Release are the same as in InterfacedObject:

function TGameRectangle._AddRef: Integer;
begin
{$IFNDEF AUTOREFCOUNT}
  Result := AtomicIncrement(FRefCount);
{$ELSE}
  Result := __ObjAddRef;
{$ENDIF}
end;

function TGameRectangle._Release: Integer;
begin
{$IFNDEF AUTOREFCOUNT}
  Result := AtomicDecrement(FRefCount);
  if Result = 0 then
    Destroy;
{$ELSE}
  Result := __ObjRelease;
{$ENDIF}
end;

To create a rectangle I do this:

var  
  lRect: TGameRectangle;
begin
  lRect := TGameRectangle.Create(self);
  lRect.Parent := Layout1;
  lRect.Align := TAlignLayout.alClient;
  FIntObj := lRect as IBoardShape;

Later I free it by setting the FIntObj to nil. On Windows when I follow_Release the reference count is 1 and the count gets decremented and the object gets freed. When running on Android the reference count is 5 when I enter into _Release (the reference count is shown inside __ObjRelease). Since the reference count is still high the object doesn't free.

I've recreated this in a very simple demo using basically just the code I've posted here. Could someone explain what's different in ARC that's causing the reference count to be so high?

Upvotes: 3

Views: 762

Answers (1)

David Heffernan
David Heffernan

Reputation: 613441

All this is simply unnecessary under ARC because ARC already counts references and controls lifetime.

Under ARC you can, and should, rely on the IInterface implementation from the base class.

Under ARC your code should look like this:

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

Your bigger problem is that your code can never work on non-ARC platforms. That's because you have a TComponent descendent that is owned. As such the owner holds a reference to the rectangle object and will attempt to destroy when it is destroyed. In addition to that, your interface reference counting also assumes ownership. As a rule, objects need to have exactly one owner. Your design gives them two.

You should, on non-ARC platforms, only manage the lifetime through interface reference counting if the object has no owner. I discussed this in more detail in my answer to your previous question.

Upvotes: 1

Related Questions