2012-07-16 33 views
17

有人可以提供一個解釋的弱參考文獻在德爾福?「弱參考」:腳踏實地解釋需要

我注意到這個概念經常在我仔細研究的一些庫/框架源代碼中提到。我處於陷阱狀態,希望對此有一個明確的理解。

回答

34

通過接口引用相互引用的實例在基於引用計數的接口實現中彼此保持活動。

一個弱點是用來打破「彼此活着」的擁抱。這是通過將一個引用聲明爲一個純指針來繞過引用計數機制來完成的。

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。如果您需要它,請確保當父對象超出範圍時,它將在它自己引用它的子對象之前讓它們的後備引用爲零。

+0

+1的解釋和您的個人資料圖片 – 2012-07-16 10:25:54

+0

@JanDoggen::-)動物一直是我最喜歡的布偶 – 2012-07-16 11:29:04

10

默認情況下在Delphi中,所有引用或者是:

  • weak referencespointerclass實例;
  • 明確的拷貝爲低電平值類型integer, Int64, currency, doublerecord(老棄用objectshortstring);
  • copy-on-writereference counting爲高級值類型(例如,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; 

你可以嘗試讀取my blog post about weak references in Delphi - 及其相關的源代碼:我們實現了直接的弱引用,並將從Delphi 6到XE2的弱引用接口處理「歸零」。

事實上,在某些情況下,如果您將引用實例釋放到其子代之前,您需要將接口弱域設置爲nil,以避免任何訪問衝突問題。這叫做「歸零弱指針」,以及我們試圖在Delphi中實現的什麼Apple implemented with the ARC model

+0

C#也有一個[WeakReference的(http://msdn.microsoft.com/de-de /library/system.weakreference.aspx)類來允許GC在沒有其他引用時清除該對象。 – 2012-07-16 16:37:23

+0

@StefanGlienke你是對的,但在GC世界中,弱引用有點多種多樣,因爲它們是用來繞過垃圾集合本身的特定類。在Delphi中,我們沒有任何GC旁路,只是對管理自己內存的對象的引用。 (Zeroing)Delphi接口中的弱指針比這個要低一些。 – 2012-07-17 05:29:02

+0

同意,我只想指出,這不僅僅是一個德爾福問題,雖然GC處理交叉/循環引用更聰明。 – 2012-07-17 05:49:11

2

在最常見的情況下,strong reference控制引用實例的生命週期,而weak reference則不控制引用實例的生命週期。術語weak reference可用於垃圾回收器,引用計數接口或通用對象的上下文中。

例如,一個Delphi窗體包含對其所有控件的引用;這些引用可以被稱爲強壯的,因爲當表單被銷燬時,它的控件也被銷燬。另一方面,Delphi表單的控件有一個它所屬的表單的引用。這個引用可能會被調用爲弱,因爲它不以任何方式控制表單的生命週期。