[教程] Delphi 中的自动引用计数使用规则

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 做的那样。

分享到: