简体   繁体   English

如何正确使用ARC接口?

[英]How to correctly use ARC with interfaces?

I have a problem understanding how ARC with Interfaces exactly work. 我在理解ARC与接口的确切工作方式时遇到了问题。 In most tutorials I have read that setting the interface to nil will call _Release , which will call Destroy , when the ARC counter reaches zero. 在大多数教程中,我已经读过将接口设置为nil将调用_Release ,当ARC计数器达到零时,它将调用Destroy I have read a few tutorials, but it is not yet clear to me. 我已经阅读了一些教程,但目前尚不清楚。

type
  IXXX = interface(IInterface)
  end;

  TXXX = class(TInterfacedPersistent, IXXX)
  public
    constructor Create;
    destructor Destroy; override;
  end;


procedure TForm1.Button4Click(Sender: TObject);
var
  intf: IXXX;
begin
  intf := TXXX.Create; // IntfCopy  will be called. Message "create"
  intf := nil;         // IntfClear will be called. No message "destroy"
end;

{ TXXX }

constructor TXXX.Create;
begin
  inherited;

  showmessage('create');
end;

destructor TXXX.Destroy;
begin
  showmessage('destroy');

  inherited;
end;

When I run this program, I will get a message "create", but no message "destroy". 当我运行这个程序时,我会收到一条消息“create”,但没有消息“destroy”。 I have looked in the assembler code and saw that calls to System._IntfCopy and System._IntfClear are made. 我查看了汇编程序代码,看到了对System._IntfCopySystem._IntfClear调用。

Looking into System._IntfClear makes clear that _Release is not called when Source is nil, which is true for intf := nil . 查看System._IntfClear清楚地表明,当Source为nil时,不会调用_Release ,这对于intf := nil是正确的。 So there is no wonder why the object is never destroyed. 所以毫无疑问为什么对象永远不会被破坏。

But how to use ARC correctly then? 但是如何正确使用ARC呢?

A blog recommends not touching interfaces and instead using only non-reference-counted versions. 博客建议不要触摸界面,而只使用非引用计数版本。 I know how to override _AddRef and _Release for bypassing/disabling ARC, but I want to use ARC, except if there is a good reason not to use it. 我知道如何覆盖_AddRef_Release来绕过/禁用ARC,但我想使用ARC,除非有充分的理由不使用它。

(Using Delphi 6) (使用Delphi 6)

The class you derive from, TInterfacedPersistent has this to say in its documentation : 你派生的类, TInterfacedPersistent在其文档中有这样的说法:

TInterfacedPersistent, like all persistent objects, supports the ability to read and write its properties to and from a stream. 与所有持久对象一样,TInterfacedPersistent支持从流中读取和写入其属性的功能。 In addition, it supplies a default implementation of the IInterface methods (_AddRef, _Release, and QueryInterface). 此外,它还提供了IInterface方法的默认实现(_AddRef,_Release和QueryInterface)。 This default implementation simply passes these calls on to the interface of the persistent object's Owner, if any. 此默认实现只是将这些调用传递给持久对象的所有者的接口(如果有)。

The call to _Release is of course made when the reference count drops to zero. 当参考计数降至零时,当然会_Release But nothing happens then because your object has no owner. 但是没有任何事情发生,因为你的对象没有所有者

If you wish to use reference counting to manage the lifetime then you should dervive from TInterfacedObject instead. 如果您希望使用引用计数来管理生命周期,那么您应该改为使用TInterfacedObject Or provide implementations of _AddRef and _Release that count references and destroy the object when the reference count drops to zero. 或者提供_AddRef_Release实现,它们在引用计数降为零时计算引用并销毁对象。 Use TInterfacedObject as the template of how to implement that. 使用TInterfacedObject作为如何实现它的模板。

For what it is worth, the article you link to contains stunningly bad advice. 对于它的价值,你链接到的文章包含了非常糟糕的建议。 Do not heed it. 不要留意它。 If you are to use interfaces, let reference counting manage lifetime, and refer to the objects only through interfaces. 如果要使用接口,请让引用计数管理生命周期,并仅通过接口引用对象。

I think you might want to use TInterfacedObject , but wrote TInterfacedPersistent instead, which does NOT manage the reference counting automatically. 我想您可能想要使用TInterfacedObject ,但是编写了TInterfacedPersistent ,它不会自动管理引用计数。

Usually when I free an interfaced object, I'll always put an assertion one line before, to make sure that the object is actually an interfaced object. 通常当我释放一个接口对象时,我总是先断言一行,以确保该对象实际上是一个接口对象。

procedure TForm1.Button4Click(Sender: TObject);
var
  intf: IXXX;
begin
  // create an interfaced object
  intf := TXXX.Create;

  // free an interfaced object
  if Assigned(intf) then
  begin
    assert(intf.InheritsFrom(TInterfacedObject));
    intf := nil;
  end;
end;

Note that TSingletonImplementation is yet another class that does not manage reference counting automatically. 请注意, TSingletonImplementation是另一个不自动管理引用计数的类。

Regarding the blog link you posted, it's reasonable for some condidtions, especially when a component or a control that is owned by another component. 关于您发布的博客链接,对某些条件是合理的,尤其是当一个组件或控件由另一个组件拥有时。

So you said that if there is a good reason not to use it. 所以你说如果有充分的理由不使用它。 There's one: 有一个:

"If your object is a component or a control that is owned by another component, then it is part of a different memory management system that is based in TComponent. Although some classes mix the object lifetime management approaches of TComponent and interface reference counting, this is very tricky to implement correctly" “如果你的对象是另一个组件所拥有的组件或控件,那么它就是基于TComponent的不同内存管理系统的一部分。虽然有些类混合了TComponent的对象生命周期管理方法和接口引用计数,正确实施是非常棘手的“

Not Using Reference Counting, the offical document said 官方文件说,不使用参考计数

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM