2011-05-04 58 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"

释放的对象
相关问题