Delphi 用户应该早已经熟悉自动引用计数的概念(ARC),Delphi 很早就开始支持接口、动态数组和字符串的自动引用计数(DCC32、DCC64、DCCOSX),移动编译器则加入对类的自动引用计数支持,因此还加了入弱引用的概念,以管理循环引用。有个预编译条件指令 AUTOREFCOUNT 可以检测当前是否编译器是否启用了自动引用计数。
在启用自动引用计数的情况下,编码发生了以下变化:
- 对象创建不再需要手工释放,所以下面的代码是没有内存泄露问题的
class procedure TMySimpleClass.CreateOnly; var MyObj: TMySimpleClass; begin MyObj := TMySimpleClass.Create; MyObj.DoSomething; end;
- 循环引用需要通过 weak 指示符,否则会造成对象无法释放。
- 如果要提前释放,使用 DisposeOf 。像下面直接设置为 nil 只是减少引用计数,如果没有其它引用,会直接释放对象,但如果有其它引用存在,就不会释放。所以,在使用 FreeAndNil 时,一定要注意这一点,在移动平台,FreeAndNil 等价于赋值为 NIL。
class procedure TMySimpleClass.SetNil; var MyObj: TMySimpleClass; begin MyObj := TMySimpleClass.Create; MyObj.DoSomething (False); // True => raise MyObj := nil; // do something else end;
- Free 方法依然可以被调用,只不过它在自动引用计数模式下,被编译器优化为 X:=nil 。
综上述,要直接立即释放一个对象,那么在自动引用计数模式上,调用 DisposeOf ,调用 Free 或 FreeAndNil 不能保证这一点。
【注意】
1、DisposeOf 是将对象置于一种特殊的“僵尸”状态,其析构函数被调用,但对象本身占用的内存空间并没有被立即回收,其占用的内存只有在其引用计数被清为 0 时才会直接的释放。
2、Delphi 的对象的引用计数最大为 230 次,有一位被用做标志是否已经调用了 DisposseOf。
3、Weak 注解不适合用于大量对象引用的场合,它虽然不会增加引用计数,但会调用内部的 RegisterWeakRef 和 UnregisterRef 函数来处理弱引用信息。这些额外的代码在创建大量对象时,它对效率的影响还是让人有点纠结的。对于这种场合,推荐用 Unsafe 注解来代替 Weak(虽然官方不太推荐)。无论是 Weak 还是 Unsafe,在使用时都要比较小心,以避免内存泄露。Unsafe 与 Weak 的的不同在于 weak 引用的对象释放时,相应的引用会被置为 NIL,而 unsafe 是不管这点事的,这也就解释了为啥 weak 注解会引发 RegisterWeakRef / UnregisterRef 调用了。
4、如果你不希望你的对象被自动管理引用计数,可以重新实现 IInterface 接口的 __AddRef 和 __Release 方法,就象 TComponent 做的那样。