2012-10-12 112 views
9

這裏我有一個棘手的情況,我想。我需要能夠釋放一個記錄字段的對象。如果它是一個類,我通常會在析構函數中寫入清理代碼。但是由於記錄類型不能引入「析構函數」,因此如何調用TObject(Field).Free;如何釋放記錄中的對象?

將會有兩種用法我預測:

  1. 用一個新的更換記錄。

    我覺得這個用法很容易實現。由於記錄是值類型,因此它們在賦值時被複制,所以我可以重載賦值運算符並釋放舊記錄擁有的對象。

    編輯:分配超載無法這是我新的信息...)

  2. 退出創紀錄的變量定義成範圍。

    我可以想到一個私有方法,可以釋放這些對象,並且可以手動調用該方法。但是,這裏是相同的問題:如何使它更記錄?這種行爲那種感覺就像一個類...

這裏是一個樣本(顯然不打算使用):

TProperties = record 
    ... some other spesific typed fields: Integers, pointers etc.. 
    FBaseData: Pointer; 

    FAdditionalData: TList<Pointer>; 
    //FAdditionalData: array of Pointer; this was the first intended definition 
end; 

假設,

FAdditionalData:=TList<Pointer>.Crete; 

稱爲在記錄構造函數中或通過公開訪問該字段手動記錄變量範圍,如

procedure TFormX.ButtonXClick(Sender: TObject); 
var 
    rec: TProperties; 
begin 
    //rec:=TProperties.Create(with some parameters); 

    rec.FAdditionalData:=TList<Pointer>.Create; 

    //do some work with rec 
end; 

退出ButtonClick範圍REC沒有更多但從TList依然保持着它的存在導致內存泄漏...

+1

記錄分配不能重載。 – kludg

+0

我沒有意識到這一點(從來沒有需要之前),但我現在瞭解它:)是的,它不能被超載... –

回答

10

如果你在唱片已經是一個對象引用後,那麼你不能讓編譯器來幫助你。你完全負責該對象的生命週期。您不能重載賦值運算符,也不會收到任何範圍確定的通知。

你可以做的是添加一個防護接口來管理對象的生命週期。

TMyRecord = record 
    obj: TMyObject; 
    guard: IInterface; 
end; 

您需要確保TMyObject通過引用計數來管理其生存期。例如從TInterfacedObject派生。

當初始化記錄你這樣做:

rec.obj := TMyObject.Create; 
rec.guard := rec.obj; 

在這一點上,記錄的guard字段現在將管理對象的生命週期。事實上,如果你想進一步推動這個想法,你可以建立一個專門的類來保護對象的生命週期。這不再限制你在你的課堂上實施IInterface。網上有很多例子可以說明這種技術。例如,我提供Jarrod Hollingworth的文章標題爲Smart Pointers和Barry Kelly的標題爲Reference-counted pointers, revisited。還有更多。這是一個古老的把戲!

但是,請注意,您在這裏是一個奇怪的值類型和引用類型的混合。表面上看,記錄是價值類型。但是,這個就像一個參考類型。如果記錄中的其他字段是值類型,那麼這會更令人困惑。在處理這樣的記錄時,您需要非常清楚這個問題。

表面上,不知道更多關於您的設計,我傾向於建議您不要將對象引用放入記錄中。它們更適合於參考類型,即類。

+7

我會建議帶領最後一段第一。如果對生命週期管理不是非常非常小心,將類放在記錄中是快速通向錯誤代碼的路徑。 – afrazier

+0

非常豐富的答案,謝謝Heffernan先生..我會試驗這些方法。我需要更新這個問題,以便使設計更加清晰。對象字段是** TList ** s,但似乎我會將其恢復到第一個地方:**指針數組** –

+1

使用動態數組解決了生命週期管理。您可以考慮使用Synopse優秀的'TDynArray',以便使用動態數組。 –

3

我記得有人創建了一個名爲TLifetimeWatcher的類。基本上,它看起來像:

TLifetimeWatcher = class(TInterfacedObject) 
private 
    fInstance: TObject; 
    fProc: TProc; 
public 
    constructor Create(instance: TObject); overload; 
    constructor Create(instance: TObject; proc: TProc); overload; 
    destructor Destroy; override; 
end; 

//的(清理)PROC將在如果已分配的析構函數被執行,否則實例將通過調用Free方法釋放。

+0

這是我回答中概述的基本方法。沒有實施,這不是很有用。 –

+2

'有人'是巴里凱利 - http://blog.barrkel.com/2008/09/smart-pointers-in-delphi.html – kludg