【问题标题】:Why TForm's _release does not call destructor?为什么 TForm 的 _release 不调用析构函数?
【发布时间】:2014-12-09 09:15:48
【问题描述】:

为什么TForm的_release方法不调用析构函数?

var
   F, U : IUnknown;

procedure TForm1.btn1Click(Sender: TObject);
begin
  U := IUnknown(TMyObject.Create);    // MyIterfacedObject (inherits TInterfacedObject)
  F := IUnknown(TMyForm.Create(nil)); 
end;

procedure TForm1.btn2Click(Sender: TObject);
begin
  U := nil;    // Calls destructor
  F._Release;  // Does not call destructor
  F := nil;    // Does not call destructor
end;

我看了一下 TInterfaceObject 和 TComponent 类的 _release 方法:

function TInterfacedObject._Release: Integer;
begin
  Result := InterlockedDecrement(FRefCount);
  if Result = 0 then
    Destroy;
end;

function TComponent._Release: Integer;
begin
  if FVCLComObject = nil then
    Result := -1   // -1 indicates no reference counting is taking place
  else
    Result := IVCLComObject(FVCLComObject)._Release;
end;

TInterfacedObject 的 _release 似乎很好理解,但是 TComponent 的 _release 是做什么的呢?对我来说似乎很奇怪......

【问题讨论】:

    标签: delphi interface vcl delphi-2006


    【解决方案1】:

    原因是TComponent 采用的策略是生命周期管理是类用户的责任,而不是由任何接口引用自动管理。该政策在TComponent._Release 中明确表达。

    function TComponent._Release: Integer;
    begin
      if FVCLComObject = nil then
        Result := -1   // -1 indicates no reference counting is taking place
      else
        Result := IVCLComObject(FVCLComObject)._Release;
    end;
    

    您所描述的常见场景具有FVCLComObject 等于nil。因此代码通过返回-1 明确表示没有引用计数。它甚至是这样评论的。

    生命周期管理需要以一种或另一种方式进行。 Delphi 代码常用的两种模式是:

    由调用者管理的生命周期

    var
      obj: TMyObject;
    ....
    obj := TMyObject.Create;
    try
      DoSomething(obj);
    finally
      obj.Free; // the object is explicitly destroyed here
    end;
    

    尽管TComponent 的用法通常与此稍有不同,因为它的构造函数传递了一个所有者组件。然后这个所有者负责销毁所拥有的组件。所以这个模式看起来像这样:

    component := TMyComponent.Create(OwnerComponent);
    ... // now component will be destroyed when OwnerComponent is destroyed
    

    由接口引用管理的生命周期

    var
      intf: IMyInterface;
    ....
    intf := TMyObject.Create;
    DoSomething(intf);
    // the implementing object behind intf is destroyed when the last 
    // interface reference leaves scope
    

    您不能混合使用这两种模式。设计选择TComponent 将遵循第一种模式。因此必须禁用接口引用计数。相比之下,TInterfacedObject 采用的是其他策略。

    【讨论】:

    • 我认为除了你的两个常用模式之外,你还应该提到所有者模式,特别是因为它在 TComponent 中使用了 OP 所指的。
    • 重新阅读您的答案后,我会说 TComponent 不遵循您的第一个示例中的模式(您在其中使用 TMyObject ),尽管可以这样做,但 TComponent 后代的固有模式是所有权模型。
    • @iamjoosy 取决于作为 Owner 传递的内容。通过nil,调用者仍然是所有者。传递除nil 以外的其他东西,然后其他东西成为所有者。本质上是一样的。
    • 所有这些设计选择是否意味着将接口与表单(以及任何其他 TComponent 的后代)一起使用是不好的做法?
    • 好吧,你只需要小心。它可能会变得混乱。您需要将由TComponent 支持的接口引用与其他接口引用区别对待。您不得在实现组件的生命周期结束后保留​​该接口引用。这可能很难安排。
    猜你喜欢
    • 2015-08-21
    • 2021-06-21
    • 2013-08-08
    • 2011-12-13
    • 2021-08-04
    • 2014-03-05
    • 2018-05-24
    相关资源
    最近更新 更多