2008-10-05 29 views
4

我有一个接口的小问题。这里是伪代码:接口“递归”和引用计数

type 
    Interface1 = interface 
    end; 

    Interface2 = interface 
    end; 

    TParentClass = class(TInterfacedObject, Interface1) 
    private 
    fChild : Interface2; 
    public 
    procedure AddChild(aChild : Interface2); 
    end; 

    TChildClass = class(TInterfacedObject, Interface2) 
    private 
    fParent : Interface2; 
    public 
    constructor Create(aPArent : Interface1); 
    end; 

任何人都可以看到缺陷吗?我需要孩子参考其父母,但引用计数在这种情况下不起作用。如果我创建一个ParentClass实例并添加一个子项,那么父类永远不会被释放。我明白为什么。我如何绕过它?

+0

请参阅http://stackoverflow.com/questions/487311/how-to-implement-reference-counted-objects-in-delphi#487387,以获得有关使用Delphi界面的循环引用问题的完整答案。 – 2012-02-01 17:28:15

+0

使用真正的GC代替引用计数。 – 2008-11-24 18:40:25

回答

10

引用计数引用有两个语义:它充当所有权的份额以及导航对象图的方式。

通常,您不需要这两种语义的在引用图的一个循环中的所有链接上。也许只有父母拥有孩子,而不是相反?如果是这样的话,就可以让孩子引用到父薄弱环节,通过将它们存储为指针,就像这样:

TChildClass = class(TInterfacedObject, Interface2) 
private 
    fParent : Pointer; 
    function GetParent: Interface1; 
public 
    constructor Create(aPArent : Interface1); 
    property Parent: Interface1 read GetParent; 
end; 

function TChildClass.GetParent: Interface1; 
begin 
    Result := Interface1(fParent); 
end; 

constructor TChildClass.Create(AParent: Interface1); 
begin 
    fParent := Pointer(AParent); 
end; 

这是安全的,如果实例的树的根是保证在某个地方保持活力,也就是说,你并不仅仅依靠保持对树的一个分支的引用,而仍然能够浏览整个树的分支。

1

您必须制定明确取消链接正确引用的方法。在这种情况下无法正确使用自动引用计数。

+0

明确的方法是不必要的;它取决于所需的参考语义。 – 2008-10-05 10:23:40

3

那么,参考计数当然是确实在这种情况下工作 - 它只是没有解决问题。

这是引用计数的最大问题 - 当你有一个循环引用时,你必须明确地'打破'它(例如,设置一个接口引用为'nil')。这也是为什么引用计数不是真正取代垃圾收集的原因 - 垃圾收集器意识到循环可能存在,并且可以在未从“外部”引用时释放这样的循环结构。

0

通过在第一个例子中使用函数指针,那么循环引用问题就不存在了。 .NET使用委托,VB6使用事件。所有这些都有利于不增加被指向的对象的引用计数。