加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 大数据 > 正文

处理Delphi中的循环强引用

发布时间:2020-12-15 09:50:43 所属栏目:大数据 来源:网络整理
导读:我有两个类(在我的示例中为TObject1和TObject2),它们通过接口(IObject1,IObject2)相互了解.正如您在Delphi中可能知道的那样,这将导致内存泄漏,因为参考计数器将始终保持在零以上.通常的解决方案是将一个引用声明为弱.这在大多数情况下都适用,因为你通常知道
我有两个类(在我的示例中为TObject1和TObject2),它们通过接口(IObject1,IObject2)相互了解.正如您在Delphi中可能知道的那样,这将导致内存泄漏,因为参考计数器将始终保持在零以上.通常的解决方案是将一个引用声明为弱.这在大多数情况下都适用,因为你通常知道哪一个会被破坏,或者一旦它被销毁就不一定需要弱引用后面的对象.

这说我尝试以这样的方式解决问题,即两个对象都保持活着,直到两者都不再被引用:(因为我使用[unsafe]属性需要Delphi 10.1)

program Project14;

{$APPTYPE CONSOLE}

uses
  System.SysUtils;

type
  IObject2 = interface;

  IObject1 = interface
    ['{F68D7631-4838-4E15-871A-BD2EAF16CC49}']
    function GetObject2: IObject2;
  end;

  IObject2 = interface
    ['{98EB60DA-646D-4ECF-B5A7-6A27B3106689}']
  end;

  TObject1 = class(TInterfacedObject,IObject1)
    [unsafe] FObj2: IObject2;
    constructor Create;
    destructor Destroy; override;

    function GetObject2: IObject2;
  end;

  TObject2 = class(TContainedObject,IObject2)
    [unsafe] FObj1: IObject1;
    constructor Create(aObj1: IObject1);
    destructor Destroy; override;
  end;

constructor TObject1.Create;
begin
  FObj2 := TObject2.Create(Self);
end;

destructor TObject1.Destroy;
begin
  TContainedObject(FObj2).Free;
  inherited Destroy;
end;

function TObject1.GetObject2: IObject2;
begin
  Result := FObj2;
end;

constructor TObject2.Create(aObj1: IObject1);
begin
  inherited Create(aObj1);
  FObj1 := aObj1;
end;

destructor TObject2.Destroy;
begin
  inherited Destroy;
end;

function Test1: IObject1;
var
  x: IObject2;
begin
  Result := TObject1.Create;
  x := Result.GetObject2;
end;

function Test2: IObject2;
var
  x: IObject1;
begin
  x := TObject1.Create;
  Result := x.GetObject2;
end;

var
  o1: IObject1;
  o2: IObject2;
begin
  try
    o1 := Test1();
    o2 := Test2();
  except
    on E: Exception do
      Writeln(E.ClassName,': ',E.Message);
  end;
end.

这确实有效..函数Test1和Test2每个创建一个TObject1和TObject2的实例互相引用,一旦o1和o2超出范围,所有实例都会被销毁.该解决方案基于TContainedObject,它将引用计数转发给“控制器”(在本例中为TObject1).

现在我知道这个解决方案存在缺陷,这就是我的问题所在:

>“TContainedObject(FObj2).Free;”闻起来有点,但我没有更好的解决方案,因为我需要使用一个接口来引用TObject2(生产代码在这一端包含一些继承).有什么想法来清理它吗?
>你很容易忘记将两个类之间的所有引用声明为弱和…
>一个类似的问题开始引发更多类:拥有一个引用的TObject3并引用另一个:内存泄漏.我可以通过让它从TContainedObject下降来处理它,但是使用遗留代码这可能不是一件容易的事.

我觉得这个解决方案不能普遍应用,并且希望有一个可以 – 或者可能是一个答案,它将描述为什么它很难甚至不可能有一个易于使用的100%解决方案来管理这种情况.
Imho,如果没有从域中引用它们而不必仔细考虑该域中的每个引用,那么有一定数量的对象会相互破坏并不复杂.

解决方法

不要使用不安全的
[unsafe]不应该在普通代码中使用.
如果您不希望编译器在接口上进行引用计数,那么对于使用它来说真的是一个hack.

使用弱而不是
如果由于某种原因你必须有循环引用,那么在其中一个引用上使用[weak]属性并像往常一样声明另一个引用.

在您的示例中,它看起来像这样:

TParent = class(TInterfacedObject,IParent)
    FChild: IChild;   //normal child
    constructor Create;
    function GetObject2: IChild;
  end;

  TChild = class(TContainedObject,IChild)
    //reference from the child to the parent,always [weak] if circular.
    [weak] FObj1: IParent;   
    constructor Create(const aObj1: IParent);
  end;

现在不需要在析构函数中做任何特殊操作,因此可以省略这些.
编译器跟踪所有弱引用,并在引用接口的引用计数达到零时将它们设置为nil.
所有这些都是以线程安全的方式完成的.
但弱引用本身并不会增加引用计数.

何时使用不安全
这与不安全的参考形成对比,其中根本不进行跟踪和参考计数.

您将在作为单例的接口类型或已禁用引用计数的接口类型上使用[unsafe]引用.
在任何情况下,引用计数都固定为-1,因此调用addref和release是一个不必要的开销.
放[不安全]消除了愚蠢的开销.
除非您的接口覆盖_addref并且_release不要使用[unsafe].

柏林之前的另类
在柏林之前,NexGen编译器之外没有[弱]属性.
如果您正在运行西雅图,2010年或以下代码之间的任何内容将{几乎}相同.
虽然我不确定此代码是否可能不会成为多线程代码中竞争条件的牺牲品.
如果这是你担心的问题,请随意举旗,我会调查.

TParent = class(TInterfacedObject,always [weak] if circular.
    FObj1: TParent;   //not an interface will not get refcounted.  
    constructor Create(const aObj1: IParent);
    destructor Destroy; override;
  end;

  constructor TChild.Create(const aObj1: IParent);
  begin
    inherited Create;
    FObject1:= (aObj1 as TParent);
  end;

 destructor TParent.Destroy;
 begin
   if Assigned(FChild) then FChild.InvalidateYourPointersToParent(self);
   inherited;
 end;

这也将确保接口得到妥善处理,但是现在TChild.FObject1不会自动被填充.您可以将代码放在TParent的析构函数中以访问其所有子代,并在显示的代码中通知它们.如果循环引用中的某个参与者无法通知其弱链接的对应方,那么您将需要设置一些其他机制来忽略那些弱引用.

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读