2011-05-04 55 views
2

我在Delphi應用程序中實現了FlyWeight模式。一切都很好,一切都快了很多,記憶力也減少了,但有一件事我很擔心。停止Delphi中釋放共享對象的客戶端代碼

只要客戶端代碼從不在調用共享對象上調用Free(),我的實現將只能工作。在Flyweight模式中,FlyweightFactory本身應該「保持對飛重的引用」,即共享對象。

我的問題是,沒有(明顯)的方法來阻止其他代碼銷燬對象,一旦他們有一個引用。我可以忍受這一點,但如果我能夠自由傳遞這些物體而不用擔心意外釋放,那將是一個「大勝利」。

要顯示(人爲)例如:

flyweight1:=FlyweightFactory.GetFlyweight(42); 
WriteLn('Description is '+flyweight.Description); 
flyweight1.Free; 

flyweight2:=FlyweightFactory.GetFlyweight(42); 
WriteLn('Description is '+flyweight.Description); 
// Object has already been Freed!; behaviour is undefined 

我已經考慮重寫析as shown here停止被完全釋放的輕量級對象。這不是我的選擇

a)我只想停止緩存對象免於被釋放,而不是不屬於緩存的對象。有很多遺留代碼不使用緩存;他們仍然需要手動創建和釋放對象。

b)I do想要FlyweightFactory在定稿期間釋放對象;我同意沃倫P的說法,「零泄漏記憶」政策是最好的。

我會從GoF的

的飛錘章報價留下

共享性意味着某種形式的 引用計數和垃圾收集 的回收存儲,當它不再需要 。但是, 這兩個都不是必要的,如果flyweights的數量是固定的而且很小。在這種情況下,飛錘值得永久保留 。

在我的情況下flyweights是「固定」和(足夠)小。

[UPDATE見我的回答對我是如何解決這個問題的詳細信息]

回答

0

我設法解決了我在原始問題中引用的問題,使用以下技術,David Heffernan在他的回答中提出了建議。

一)我只想 從被釋放,而不是對象 不是緩存的一部分停止緩存的對象。有很多遺留代碼不使用 緩存;他們仍然需要手動創建 和自由對象。

我解決了這個問題,通過子類化Flyweight類,並在子類中重寫destroy,BeforeDestruction和FreeInstance。這樣就離開了父類。緩存包含子類的實例(不能被釋放),而緩存之外的對象可以像往常一樣被釋放。

b)我確實想要FlyweightFactory到 在最終化過程中釋放對象; 我同意沃倫P,「零 泄漏內存」政策是最好的。

爲了解決這個問題,我添加了一個必須設置爲true的私有布爾標誌,才能釋放對象。該標誌只能從高速緩存單元設置,對其他代碼不可見。這意味着標誌不能由緩存外的代碼設置在外部。

析構函數看起來就像這樣:

destructor TCachedItem.destroy; 
begin 
    if destroyAllowed then 
     inherited; 
end; 

如果客戶端代碼改掉免費緩存對象,調用將沒有任何效果。

+0

換句話說,你做了我的建議。 – 2013-01-15 00:17:42

2

My answerquestion you link to仍然適用。對象必須通過一個私有布爾標誌來知道它們是緩存的對象。然後他們可以選擇不要在DestroyFreeInstance中摧毀自己。如果你想允許Free被調用,那麼真的沒有其他選擇。

要處理最終化,您需要將緩存對象添加到緩存對象列表中。該對象列表可以在定稿時釋放。當然,當你走進名單時,禁用釋放的標誌將不得不被重新設置。

在完成這一點之後,我建議你註冊一個預期的內存泄漏並且泄漏這個內存。它使代碼更簡單,沒有任何東西可以丟失。一旦你的可執行文件關閉,任何你沒有釋放的內存將被操作系統回收。有一點要注意:如果你的代碼被編譯成一個DLL,那麼如果你的DLL被加載,卸載,重新加載等,泄漏可能會很麻煩。

這一切告訴你的是你正在逆流而上。是否有可能通過一種不同的解決方案來實現您的目標,這種解決方案更符合德爾福的指導方針?

+0

@David:link?我想你也排除了接口? – 2011-05-04 03:05:30

+0

@moz OP顯然應該使用對象。我懷疑可能有更自然的方法來解決問題,而不是破壞對象的破壞。 – 2011-05-04 03:07:40

+0

@大衛:我希望如此。我要求提供一個鏈接到上一個問題 - 這個用戶有一些沒有,我也沒有跳過。 – 2011-05-04 03:20:18

0

我建議添加一個引用計數,以便知道您的共享對象是否仍然被使用。 每個客戶端都應該使用AddRef/Release模式(AddRef增加計數;發佈遞減;如果計數到零,則釋放被調用)

AddRef可以由您的GetFlyweight方法直接調用;必須使用發佈而不是免費。

如果你重構你的類,並從中提取一個接口,然後在接口實現中自然實現的AddRef/Release模式。 (你可以從TInterfacedObject派生或由你自己實現IInterface)

0

理想情況下,你很少想要2種使用相同事物的方法。從長遠來看,這隻會讓問題複雜化。在6個月的時間裏,你可能不確定一段特定的代碼是使用新的flyweight範式還是舊的範例。

最好的辦法防止有人叫FreeDestroy是確保它甚至不在那裏。在Delphi世界中,唯一的方法就是使用interfaces

要擴大你的人爲的例子:

type 
    TFlyweightObject = class 
    public 
    constructor Create(ANumber: Integer); 
    function Description: string; 
    end; 

    TFlyweightFactory = class 
    public 
    function GetFlyweight(ANumber: Integer): TFlyweightObject; 
    end; 

這被物體很容易被惡意的客戶端destoyed。您可以進行以下更改:

type 
    IFlyweight = interface 
    //place guid here 
    function Description: string; 
    end; 

    TFlyweightObject = class(TInterfacedObject, IFlyweight) 
    public 
    constructor Create(ANumber: Integer); 
    function Description: string; 
    end; 

    TFlyweightFactory = class 
    public 
    function GetFlyweight(ANumber: Integer): IFlyweight; 
    end; 

現在任何更新爲使用flyweight範例的代碼都被迫按預期使用它。識別仍然需要重構的舊代碼也更容易,因爲它不使用接口。舊代碼仍然會直接構建「flyweight」對象。

+0

你說這是唯一的方法。總是有記錄。 – 2011-05-04 12:05:29

+0

@David:你說得對 - 記錄不能被破壞。如果你願意放棄適用於輕量級___對象_的某些概念,你就可以使用它們。 ;) – 2011-05-04 14:12:45

+0

@craig接口也不是對象。說了這麼多,我並不想向OP推薦任何明確具有許多使用對象引用的現有代碼的OP。 – 2011-05-04 14:17:32

0

你也可以通過使protectedprivate隱藏析構函數。程序員不會看到它在其所在宣佈該單位的範圍之內。

但我張貼這個答案更像是一種好奇,因爲這不會阻止通過使用FreeAndNil或使用"Protected Hack"

釋放的對象
相關問題