“弱引用”:脚踏实地需要解释(“Weak reference”: down to earth exp

2019-07-30 10:34发布

有人可以提供在Delphi 弱引用的解释?

我注意到,这个概念在某些库/框架源代码,我端详经常提到的。 我在地狱,并希望有它的一个明确的认识。

Answer 1:

通过接口引用相互引用情况下保持彼此活着的引用计数基于接口的实现。

弱引用是用来打破的“留住对方活着”熊抱。 这是通过声明一个参考作为纯指针规避引用计数机制来完成。

IFriend = Interface(IInterface)
end;

TFriend = class(TInterfacedObject, IFriend)
private
  FFriend: IFriend;
end;


var
  Peter: IFriend;
  John: IFriend;
begin
  Peter := TFriend.Create;
  John := TFriend.Create;

  Peter.Friend := John;
  John.Friend := Peter;
end;

即使当彼得和约翰走出去的范围,它们的实例围绕保持,因为它们之间的相互引用保持跌落至零的引用计数。

问题是在复合模式中更常见(父 - 子关系),其中孩子有一个反向引用父:

ISomething = Interface(IInterface)
end;

TSomething = class(TInterfacedObject, ISomething)
end;

TParent = class(TSomething)
  FChildren: TInterfacedList;
end;

TChild = class(TSomething)
  FParent: ISomething;
end;

再次,家长和孩子可以,因为它们之间的相互引用保持跌落至零的引用计数保持海誓山盟左右。

这解决了一个weak reference

TChild = class(TSomething)
  FParent: Pointer;
end;

通过声明的FParent作为一个“纯粹”的指针引用计数机制没有发挥作用的反向引用父。 当父超出范围,它的引用计数现在可以下降到零,因为它的孩子不再保持在零以上的引用计数。

注意:此解决方案并需要特别注意生命周期管理。 当在这些类的“外部”的东西保留到孩子的引用孩子们可以随时活着超出了父母的续航时间。 而当孩子承担父母引用总是指向一个有效的实例,这可能导致各种有趣的AV的。 如果你需要它,确保当父超出范围,这让孩子们自己无回引才尼尔斯自己的引用它的孩子。



Answer 2:

默认情况下,在Delphi中,所有引用或者是:

  • 弱引用pointerclass实例;
  • 为低电平值类型明确的复制 integer, Int64, currency, doublerecord (老弃用objectshortstring );
  • 写入时复制与参考计数为高电平值类型 (例如string, widestring, variant动态数组 );
  • 与参考计数强参考 interface实例;

具有较强的引用计数的主要问题是潜在循环引用的问题。 这发生在一个interface具有很强的参考到另一个,但目标interface具有很强的指针返回到原始。 即使所有其他引用都被删除,他们仍然将就彼此并不会被释放。 这也可以间接发生的,由可能在链涉及到早期的对象的最后一个对象链。

例如见下面的接口定义:

  IParent = interface
    procedure SetChild(const Value: IChild);
    function GetChild: IChild;
    function HasChild: boolean;
    property Child: IChild read GetChild write SetChild;
  end;

  IChild = interface
    procedure SetParent(const Value: IParent);
    function GetParent: IParent;
    property Parent: IParent read GetParent write SetParent;
  end;

下面的实施将明确泄漏内存:

procedure TParent.SetChild(const Value: IChild);
begin
  FChild := Value;
end;

procedure TChild.SetParent(const Value: IParent);
begin
  FParent := Value;
end;

在Delphi中,最常见的一种参考拷贝变量(即变种,动态数组或字符串)通过执行写入时复制解决这个问题。 不幸的是,这种模式是不适用的接口,这是不值的对象,但参考对象,绑一个实现类,这是不能被复制。

请注意, 垃圾收集器基于语言 (如Java或C#)不存在这个问题,因为循环引用是由它们的内存模型处理的:对象生命周期是由内存管理器在全球范围保持不变。 当然,它会增加存储器的使用,减速过程中因分配和分配过程中的附加动作(所有对象以及它们的引用必须在内部列表被保持),并且当垃圾收集器在动作进入应用可能会变慢。

与没有垃圾收集(如Delphi的)语言的一种常见解决方案是使用弱指针 ,通过该接口被分配给一个属性不增加引用计数。 为了轻松地创建一个弱指针,下面的函数可用于:

procedure SetWeak(aInterfaceField: PIInterface; const aValue: IInterface);
begin
  PPointer(aInterfaceField)^ := Pointer(aValue);
end;

因此,它可以被用作这样的:

procedure TParent.SetChild(const Value: IChild);
begin
  SetWeak(@FChild,Value);
end;

procedure TChild.SetParent(const Value: IParent);
begin
  SetWeak(@FParent,Value);
end;

你可以试着去阅读我的博客文章关于德尔福弱引用 -及其相关的源代码:我们已经实现了直接的弱引用,以及“零”弱引用接口处理来自德尔福6可达XE2。

事实上,在某些情况下,你需要的界面弱场设为nil ,如果其子之前释放的参考实例,以避免任何访问冲突问题。 这被称为“ 归零弱指针 ”,什么苹果与ARC模型实现的 ,而我们试图在Delphi中实现。



Answer 3:

也可以看看

在Delphi中移动编译器自动引用计数

其中包括新的文档[weak]属性:

对于ARC另一个重要概念是弱引用,您可以通过[弱]属性标记它们创建的角色。



Answer 4:

在最一般的情况下,一个strong reference而控制引用实例的生命周期weak reference没有。 术语weak reference可以在垃圾收集器,引用计数接口或通用对象的上下文中使用。

例如,德尔福的形式持有其所有控件的引用; 这些引用可以被称为强烈,因为当窗体被破坏它的控制也被破坏了。 在另一方面,一个Delphi形式的控制具有它属于一种形式的引用。 该参考可以称之为弱,因为它不控制以任何方式窗体的寿命。



文章来源: “Weak reference”: down to earth explanation needed