处理Delphi中的循环强引用
我有两个类(在我的示例中为TObject1和TObject2),它们通过接口(IObject1,IObject2)相互了解.正如您在Delphi中可能知道的那样,这将导致内存泄漏,因为参考计数器将始终保持在零以上.通常的解决方案是将一个引用声明为弱.这在大多数情况下都适用,因为你通常知道哪一个会被破坏,或者一旦它被销毁就不一定需要弱引用后面的对象.
这说我尝试以这样的方式解决问题,即两个对象都保持活着,直到两者都不再被引用:(因为我使用[unsafe]属性需要Delphi 10.1) program Project14; {$APPTYPE CONSOLE} uses System.SysUtils; type IObject2 = interface; IObject1 = interface ['{F68D7631-4838-4E15-871A-BD2EAF16CC49}'] function GetObject2: IObject2; end; IObject2 = interface ['{98EB60DA-646D-4ECF-B5A7-6A27B3106689}'] end; TObject1 = class(TInterfacedObject,IObject1) [unsafe] FObj2: IObject2; constructor Create; destructor Destroy; override; function GetObject2: IObject2; end; TObject2 = class(TContainedObject,IObject2) [unsafe] FObj1: IObject1; constructor Create(aObj1: IObject1); destructor Destroy; override; end; constructor TObject1.Create; begin FObj2 := TObject2.Create(Self); end; destructor TObject1.Destroy; begin TContainedObject(FObj2).Free; inherited Destroy; end; function TObject1.GetObject2: IObject2; begin Result := FObj2; end; constructor TObject2.Create(aObj1: IObject1); begin inherited Create(aObj1); FObj1 := aObj1; end; destructor TObject2.Destroy; begin inherited Destroy; end; function Test1: IObject1; var x: IObject2; begin Result := TObject1.Create; x := Result.GetObject2; end; function Test2: IObject2; var x: IObject1; begin x := TObject1.Create; Result := x.GetObject2; end; var o1: IObject1; o2: IObject2; begin try o1 := Test1(); o2 := Test2(); except on E: Exception do Writeln(E.ClassName,': ',E.Message); end; end. 这确实有效..函数Test1和Test2每个创建一个TObject1和TObject2的实例互相引用,一旦o1和o2超出范围,所有实例都会被销毁.该解决方案基于TContainedObject,它将引用计数转发给“控制器”(在本例中为TObject1). 现在我知道这个解决方案存在缺陷,这就是我的问题所在: >“TContainedObject(FObj2).Free;”闻起来有点,但我没有更好的解决方案,因为我需要使用一个接口来引用TObject2(生产代码在这一端包含一些继承).有什么想法来清理它吗? 我觉得这个解决方案不能普遍应用,并且希望有一个可以 – 或者可能是一个答案,它将描述为什么它很难甚至不可能有一个易于使用的100%解决方案来管理这种情况. 解决方法
不要使用不安全的
[unsafe]不应该在普通代码中使用. 如果您不希望编译器在接口上进行引用计数,那么对于使用它来说真的是一个hack. 使用弱而不是 在您的示例中,它看起来像这样: TParent = class(TInterfacedObject,IParent) FChild: IChild; //normal child constructor Create; function GetObject2: IChild; end; TChild = class(TContainedObject,IChild) //reference from the child to the parent,always [weak] if circular. [weak] FObj1: IParent; constructor Create(const aObj1: IParent); end; 现在不需要在析构函数中做任何特殊操作,因此可以省略这些. 何时使用不安全 您将在作为单例的接口类型或已禁用引用计数的接口类型上使用[unsafe]引用. 柏林之前的另类 TParent = class(TInterfacedObject,always [weak] if circular. FObj1: TParent; //not an interface will not get refcounted. constructor Create(const aObj1: IParent); destructor Destroy; override; end; constructor TChild.Create(const aObj1: IParent); begin inherited Create; FObject1:= (aObj1 as TParent); end; destructor TParent.Destroy; begin if Assigned(FChild) then FChild.InvalidateYourPointersToParent(self); inherited; end; 这也将确保接口得到妥善处理,但是现在TChild.FObject1不会自动被填充.您可以将代码放在TParent的析构函数中以访问其所有子代,并在显示的代码中通知它们.如果循环引用中的某个参与者无法通知其弱链接的对应方,那么您将需要设置一些其他机制来忽略那些弱引用. (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |