gpt4 book ai didi

delphi - TObject 对于在对象销毁时清除接口(interface)字段提供什么保证?

转载 作者:行者123 更新时间:2023-12-03 15:02:21 25 4
gpt4 key购买 nike

这里是一些示例代码,它是 Delphi 中的一个独立控制台应用程序,它创建一个对象,然后创建一个 TInterfacedObject 对象,并将 Interface 引用分配给 TObject 中的字段:

program ReferenceCountingProblemProject;
{$APPTYPE CONSOLE}
{$R *.res}
uses
System.SysUtils;
type
ITestInterface = interface
['{A665E2EB-183C-4426-82D4-C81531DBA89B}']
procedure AnAction;
end;
TTestInterfaceImpl = class(TInterfacedObject,ITestInterface)
constructor Create;
destructor Destroy; override;

// implement ITestInterface:
procedure AnAction;
end;

TOwnerObjectTest = class
public
FieldReferencingAnInterfaceType1:ITestInterface;
end;
constructor TTestInterfaceImpl.Create;
begin
WriteLn('TTestInterfaceImpl object created');
end;
destructor TTestInterfaceImpl.Destroy;
begin
WriteLn('TTestInterfaceImpl object destroyed');
end;
procedure TTestInterfaceImpl.AnAction;
begin
WriteLn('TTestInterfaceImpl AnAction');
end;
procedure Test;
var
OwnerObjectTest:TOwnerObjectTest;
begin
OwnerObjectTest := TOwnerObjectTest.Create;
OwnerObjectTest.FieldReferencingAnInterfaceType1 := TTestInterfaceImpl.Create as ITestInterface;
OwnerObjectTest.FieldReferencingAnInterfaceType1.AnAction;
OwnerObjectTest.Free; // This DOES cause the clearing of the interface fields automatically.
ReadLn; // wait for enter.
end;
begin
Test;
end.

我编写这段代码是因为我不确定在一些简单的例子中,Delphi 是否总是会清除我的接口(interface)指针。这是程序运行时的输出:

TTestInterfaceImpl object created
TTestInterfaceImpl AnAction
TTestInterfaceImpl object destroyed

这是我非常希望看到的输出。我编写这个程序的原因是因为我发现我正在开发的大型 Delphi 应用程序中违反了“我和 Delphi 之间的契约(Contract)”。我看到对象没有被释放,除非我在析构函数中明确地将它们归零,如下所示:

 destructor TMyClass.Destroy;
begin
FMyInterfacedField := nil; // work around leak.
end;

我相信 Delphi 正在尽最大努力将这些接口(interface)归零,因此,当我在上面的测试代码中的析构函数上设置断点时,我得到以下调用堆栈:

ReferenceCountingProblemProject.TTestInterfaceImpl.Destroy
:00408e5f TInterfacedObject._Release + $1F
:00408d77 @IntfClear + $13
ReferenceCountingProblemProject.ReferenceCountingProblemProject

正如您所看到的,正在生成对 @IntfClear 的调用,但上面的调用堆栈中缺少“Free”,这让我有点困惑,因为这两者似乎存在因果关系,但是不直接在彼此的调用路径中。这表明编译器本身在调用析构函数 TObject.Free 之后的某个时刻在我的应用程序中发出 @IntfClear 。我正确地阅读了这个标志吗?

我的问题是:Delphi 的 TObject 是否始终保证接口(interface)类型字段的最终确定?如果没有,什么时候我的接口(interface)会被清除,什么时候我必须手动清除它?接口(interface)引用的最终确定是作为 TObject 的一部分实现的,还是作为某些通用编译器范围语义的一部分实现的?事实上,关于何时手动将接口(interface)清零以及何时让 Delphi 为我做这件事,我应该遵循哪些规则?想象一下,我的应用程序中有(正如我确实有的)200 多个将接口(interface)存储为字段的类。我是否在析构函数中将它们全部设置为 Nil?我如何决定要做什么?

我的怀疑是,(a)TObject 提供了这种保证,但条件是,如果你做了一些愚蠢的事情,并且不知何故没有在包含接口(interface)引用字段的对象上调用 TObject.Destroy,那么你就会泄漏两者,或者(b)低于 TObject 级别的编译器在超出范围的级别上提供了这种语义保证,而正是这一面让我摸不着头脑,无法解释我的复杂场景现实世界中可能会遇到。

对于一些微不足道的情况,比如我从上面的演示中删除 OwnerObjectTest.Free; 的情况,并且您泄漏了演示代码创建的两个对象,我可以毫无问题地理解该语言的行为/compiler/runtime,但我希望确保我已完全理解与接口(interface)类型的对象中的字段相关的契约(Contract)或保证(如果有)。

更新通过单步执行并声明我自己的析构函数,我能够获得不同的调用堆栈,这更有意义:

ReferenceCountingProblemProject.TTestInterfaceImpl.Destroy
:00408e5f TInterfacedObject._Release + $1F
:00408d77 @IntfClear + $13
:00405483 TObject.Free + $B
ReferenceCountingProblemProject.ReferenceCountingProblemProject

这似乎表明 @IntfClear 是由 TObject.Free 调用的,这是我非常期望看到的。

最佳答案

当执行对象的析构函数时,对象实例的所有字段都将被最终确定。这是由运行时保证的。事实上,托管类型的所有字段都在销毁时完成。

此类引用计数对象未被销毁的可能解释是:

  1. 析构函数没有被执行,或者
  2. 其他东西持有对该对象的引用。

关于delphi - TObject 对于在对象销毁时清除接口(interface)字段提供什么保证?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20860548/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com