我在几年前写过一篇文章:Delphi 接口使用中,对象生命周期管理,如何释放需要注意的问题
在这篇文章的结尾部分,我提到如果有交叉引用接口的情况,要注意释放顺序。那还是 TComponent 的情况下。如果是普通的继承自 TInterfacedObject 的接口对象,这个问题更复杂。很可能会因为交叉引用接口,最后释放接口,但两个交叉引用的对象不会被释放掉。
现在,Delphi 不知道从哪个版本开始,为接口增加了关键字:Weak 和 UnSafe。
关于这两个关键字,这里有篇文章讲得比较清楚了:Weak and Unsafe Interface References in Delphi 10.1 Berlin
大意是:
普通的情况:
接口 A 对应的对象 OA 引用了接口 B。实现接口 B 的对象这里叫它 OB。
然后 OB 里面引用了 A。
如果:A := nil;照理 OA 会自动被释放,但因为 A 还有个引用在 OB 里面。因此 A 的引用计数是 1 而不是 0, OA 不会被自动释放;
然后,B := nil;但是,接口 B 还有一个引用在 OA 里面,而 OA 没有被释放,导致了 OB 也不会被释放。死锁了。
在这种情况下,如果我们强行做一个 OA.Free,将对象 OA 释放掉,这样会导致 OA 引用的接口 B 也被释放,使得接口 B 的引用计数变成了0,导致了 OB 被释放。而 OB 被释放就会释放它引用的接口 A,导致 OA 被释放,但是,这个时候,OA 已经被释放了。释放一个已经被释放的对象,结果就是得到一个 AV 异常。
这种情况下,使用新的语法,将其中一个接口定义成 UnSafe 或者 Weak 就能解决上面的交叉引用带来的死锁问题。具体用法,请看上面那篇英文的文章。