Free 的迷思
作者:高张远瞩(HiLoveS)
博客:http://www.cnblogs.com/hiloves/
转载请保留该信息
在初学Delphi时,看到论坛上一帮高手在讨论Destroy和Free,本着虚心的态度就去围观了一下。得到的结论大体是:Free也是调用Destroy,但它会事先检查该对象是否存在,如果对象存在,它才会调用Destroy。因此,程序中应该尽量使用Free来释放对象,这样更加安全一些。这就可以避免一些手误导致的错误,比方某个对象还没创建就销毁它了,比方销毁代码不小心写了两遍,按照上面Free的好处,这些都可以避免了。本人谨记在心,写代码时也身先士卒,不时想起高手们的谆谆教导。但是,越写越心惊,不知如何是好?
疑问1:为什么要用Free取代Destroy?
看看TObject.Free的代码就可以看出,Free本身不会销毁对象,它也是调用Destroy,但它会事先检查对象是否存在,如果不存在就不执行析构函数。如果是用Destroy,它才不管对象存不存在,就傻傻的去析构了,如果对象不存在不出错才怪呢。所以使用Free可以减少手误造成的错误。
但就是下面的代码还是出错了
Code: procedure Test6; var TestObj6: TTest; begin TestObj6.Free; //出错 end; procedure Test7; var TestObj7: TTest; begin TestObj7 := TTest.Create; TestObj7.Free; TestObj7.Free; //重复析构,出错 end;
不能够啊?按照上面的说法不可能出错啊?为什么呢?
疑问2:为什么Free不起作用?
判断对象是否存在的依据是什么?(可以参看另一篇文章《Assigned 的疑问》)不是这个对象的本体是否存在,也不是判断分配给对象的那片内存块是否有数据,而是对象标识符这个指针是否指向某个内存地址,也就是判断对象标识符是否不等于nil。而事实是,声明一个全局对象时Delphi会自动将其指向nil,声明一个局部对象时Delphi会将其指向一个随机的地址而不是nil,当销毁一个对象时Delphi不会自动对对象标识符做什么,原来指哪里销毁后还是指哪里。所以上面代码中刚刚声明的TestObj6和销毁后的TestObj7都不等于nil,一个指到一个不知所谓的地方,另一个还指着已经销毁的内存。如果TestObj6是个全局对象,TestObj6.Free就不会出错了。
原因清楚了,如何解决呢?
疑问3:Free不起作用,怎么办?
更改如下:
Code: procedure Test6; var TestObj6: TTest; begin TestObj6 := nil; //声明后立即赋为nil TestObj6.Free; //TestObj6是nil,不会调用Destroy end; procedure Test7; var TestObj7: TTest; begin TestObj7 := TTest.Create; FreeAndNil(TestObj7); //使用FreeAndNil取代Free,Destroy后设为nil FreeAndNil(TestObj7); //TestObj7被Destroy了,且为nil,不会再次被Destroy 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