使用ARC和不使用ARC释放接口对象的区别 - Delphi

3

我正在处理一个项目,在这个项目中我有一个如下所示的接口TRectangle:

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_ReleaseInterfacedObject中的相同:

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;

创建矩形的方法如下:

我这样做:

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

稍后我通过将 FIntObj 设置为nil来释放它。在Windows上,当我执行_Release时,引用计数为1,然后计数递减并释放对象。但在Android上运行时,当我进入_Release时,引用计数为5(引用计数显示在__ObjRelease内部)。由于引用计数仍然很高,因此对象无法释放。
我使用基本上只有我在这里发布的代码来重新创建了一个非常简单的演示。有人能解释一下在ARC中有什么不同,导致引用计数如此之高吗?

在使用ARC时,每个对实例的引用都会增加RefCount。您正在传递一个Owner和一个Parent,它们都至少持有一个对该实例的引用。加上您的接口引用,您至少有3个引用 - 但肯定还有更多由Owner和Parent引起的引用。 - Sir Rufo
2
@SirRufo...除非您使用[weak]引用,否则会出现需要解决循环引用的情况。请参阅Marco的文章。在我的看法中,Delphi内存模型的这种破坏性变更是有史以来最糟糕的想法之一。 - Arnaud Bouchez
@ArnaudBouchez Delphi太过害怕变化,尤其是破坏性变化。我渴望桌面编译器引入ARC的到来。使用[weak]属性没有任何问题。 - Günther the Beautiful
1个回答

1
在ARC下,所有这些都是不必要的,因为ARC已经计算了引用并控制了生命周期。
在ARC下,您可以并且应该依赖于基类中的IInterface实现。
在ARC下,您的代码应该像这样:
TGameRectangle = class(TRectangle, IBoardShape)
private
  FColor: integer;
  procedure SetColor(const aColor: integer);
  function GetColor: integer;
  property Color: integer read GetColor write SetColor;
end;

你的更大问题是,你的代码在非ARC平台上永远无法运行。这是因为你有一个TComponent后代是被拥有的。因此,所有者持有对矩形对象的引用,并将在其销毁时尝试销毁它。除此之外,你的接口引用计数也假定了所有权。作为规则,对象需要有恰好一个所有者。你的设计给了它们两个。
在非ARC平台上,如果对象没有所有者,你应该只通过接口引用计数来管理生命周期。我在回答中更详细地讨论了这个问题。

TRectangle的构造函数中的AOwner参数传递为nil,这样不就解决了吗? - Günther the Beautiful
@Günther the Beautiful的确会这样做。但他没有这样做。 - David Heffernan
感谢您的帮助,无论是在这个答案中还是其他的。我尝试传递nil,但是一旦创建对象的方法退出(在System的_IntfCopy内部),我就会收到异常。我不认为我会尝试这样做(就像您所说的,这感觉像是违背了设计,使用自己的方法),我只会跟踪该对象。 - Sentient

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接