作者:高张远瞩(HiLoveS)

博客:http://www.cnblogs.com/hiloves/

转载请保留该信息

在初学Delphi时,看到论坛上一帮高手在讨论Destroy和Free,本着虚心的态度就去围观了一下。得到的结论大体是:Free也是调用Destroy,但它会事先检查该对象是否存在,如果对象存在,它才会调用Destroy。因此,程序中应该尽量使用Free来释放对象,这样更加安全一些。这就可以避免一些手误导致的错误,比方某个对象还没创建就销毁它了,比方销毁代码不小心写了两遍,按照上面Free的好处,这些都可以避免了。本人谨记在心,写代码时也身先士卒,不时想起高手们的谆谆教导。但是,越写越心惊,不知如何是好?

疑问1:为什么要用Free取代Destroy?

看看TObject.Free的代码就可以看出,Free本身不会销毁对象,它也是调用Destroy,但它会事先检查对象是否存在,如果不存在就不执行析构函数。如果是用Destroy,它才不管对象存不存在,就傻傻的去析构了,如果对象不存在不出错才怪呢。所以使用Free可以减少手误造成的错误。

但就是下面的代码还是出错了

01 Code:
02 procedure Test6;
03 var
04   TestObj6: TTest;
05 begin
06   TestObj6.Free;  //出错
07 end;
08   
09 procedure Test7;
10 var
11   TestObj7: TTest;
12 begin
13   TestObj7 := TTest.Create;
14   TestObj7.Free;
15   
16   TestObj7.Free;  //重复析构,出错
17 end;

不能够啊?按照上面的说法不可能出错啊?为什么呢?

疑问2:为什么Free不起作用?

判断对象是否存在的依据是什么?(可以参看另一篇文章《Assigned 的疑问》)不是这个对象的本体是否存在,也不是判断分配给对象的那片内存块是否有数据,而是对象标识符这个指针是否指向某个内存地址,也就是判断对象标识符是否不等于nil。而事实是,声明一个全局对象时Delphi会自动将其指向nil,声明一个局部对象时Delphi会将其指向一个随机的地址而不是nil,当销毁一个对象时Delphi不会自动对对象标识符做什么,原来指哪里销毁后还是指哪里。所以上面代码中刚刚声明的TestObj6和销毁后的TestObj7都不等于nil,一个指到一个不知所谓的地方,另一个还指着已经销毁的内存。如果TestObj6是个全局对象,TestObj6.Free就不会出错了。

原因清楚了,如何解决呢?

疑问3:Free不起作用,怎么办?

更改如下:

01 Code:
02 procedure Test6;
03 var
04   TestObj6: TTest;
05 begin
06   TestObj6 := nil//声明后立即赋为nil
07   TestObj6.Free;  //TestObj6是nil,不会调用Destroy
08 end;
09   
10 procedure Test7;
11 var
12   TestObj7: TTest;
13 begin
14   TestObj7 := TTest.Create;
15   FreeAndNil(TestObj7);  //使用FreeAndNil取代Free,Destroy后设为nil
16   FreeAndNil(TestObj7);  //TestObj7被Destroy了,且为nil,不会再次被Destroy
17 end;

对于局部对象,声明后立即赋为nil,同时用FreeAndNil取代Free,FreeAndNil先调用Free再将对象指向nil,因此它不仅具有Free的好处,还会自动将对象指向nil,它的安全性比Free更好。其实在Free后,马上加一句TestObj7 := nil也行,但我觉得多此一举有点累赘。当我做到这一步,一个可怕的念头浮现出来,Delphi将TObject.Free设为public的目的是什么?难道真像高手们说的一样是开放来供程序员调用的吗?我越来越相信是开放来供FreeAndNil调用的。

 

上述测试代码下载地址:https://files.cnblogs.com/hiloves/AssignedFreeCode.rar