接口有一个小问题。这是伪代码:
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 :(得分: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;
如果实例树的根保证在某处保持活动,这是安全的,即你不依赖于只保留对树的一个分支的引用,并且仍然能够导航整个树的分支。 / p>
答案 1 :(得分:3)
嗯,当然的引用计数在这种情况下工作 - 它只是没有解决问题。
这是引用计数的最大问题 - 当你有一个循环引用时,你必须明确地'打破'它(例如,将一个接口引用设置为'nil')。这也是为什么引用计数实际上不是垃圾收集的替代品 - 垃圾收集器意识到循环可能存在并且当它们没有从“外部”引用时可以释放这种循环结构。
答案 2 :(得分:1)
您必须创建一个明确取消链接正确引用的方法。在这种情况下,无法使自动引用计数正常工作。
答案 3 :(得分:0)
在第一个示例中使用了函数指针,则循环引用问题不存在。 .NET使用委托,VB6使用事件。所有这些都具有不增加被指向对象的引用计数的好处。