先谢谢blazingfire在刚才的贴子中,突然想到一个关于FreeAndNil的问题
众所周知:FreeAndNil:=(Object.Free)+(Object:=Nil)
让我们先看一下FreeAndNil和Free的实现
procedure FreeAndNil(var Obj);
var
  Temp: TObject;
begin
  Temp := TObject(Obj);
  Pointer(Obj) := nil;
  Temp.Free;
end;
procedure TObject.Free;
begin
  if Self <> nil then
    Destroy;
end;
显然是先(Object:=Nil)而后(Object.Free)
这带来一个问题:注意Free过程,是先判断对象引用是否为空,而后Destroy的,
结论是经过FreeAndNil的,if Self<>nil then  为永假式!从来就不执行Destroy~最终结论:
在释放对象时仅将对象引用置为空就OK了?那Delphi如何在堆链表中将该块地址置为空闲状态,以便以后使用!

解决方案 »

  1.   

    .....Obj是为nil了
    但free的是Temp,判断的也是Temp是否为nil.....
      

  2.   

    内存里有一个对象的实例(可以理解为是一块分配好的内存空间),Obj和Temp都是指向这个空间的指针. free是释放对象实例所占用的内存,而nil其实integer的0,不信你可以showmessage(inttostr(integer(nil)));一下.temp:=nil;等于是让这个指针指向内存的0地址.这个地址是不允许用户访问的,所以系统遇到这样的指针就认为是无效的.
      

  3.   

    FreeAndNil并非LZ理解的“显然是先(Object:=Nil)而后(Object.Free)”,而是,Temp保存了Obj的对象,再置Obj为nil,最后释放Temp的,这里,置Obj为nil并不代表Temp也为nil。所以,除非Obj在传入时就为nil,否则,if Self <> nil then肯定为真!
      

  4.   

    先将对象保存起来,然后将指向对象的指针置空(0),然后Free......
      

  5.   

    前下面这个代码
    procedure TForm1.Button2Click(Sender: TObject);
    var
      p1, p2: Pchar;
    begin
      p1 := AllocMem(100);
      p2 := p1;
      p1 := nil;
      FreeMem(p2);
    end;
    这个和FreeAndNil是相同的道理。
      

  6.   

    先谢谢楼上大哥们!
    目前我的核心问题
    1.
    FreeAndNil这么写不行?procedure FreeAndNil(var Obj); 
    begin 
      TObject(Obj).Free; 
      Pointer(Obj) := nil; 
    end; 
    为什么要用一个Temp2.
    Obj.Free会不会不执行Obj本类的析构函数,而直接直接根类的Free
    ------
    这个问题以前记得有人问过,以前都没搞懂~
      

  7.   

    如果obj.Free当中的操作过于复杂,特别是有异步操作,或者如Application.ProcessMessage,如此可能产生消息重入的,当传进来的对象指针是一个全局的,问题就被复杂化了。而使用一个temp,就能够避免不必要的野指针出现。
      

  8.   

    我的理解:1.
    TObject(Obj).Free; 
    Pointer(Obj) := nil; 
    这样写,如果在free时出异常,:= nil就不会被执行了所以首先确保Pointer(Obj):=nil; 再去free2.
    析构函数是Destroy,不是free.只是free里面调用了Destroy. 除非你给你的类定义自己的free,否则都是执行Tojbect的free,而Tobject的free调用Tobject的Destroy,这就是为什么delphi里面把Tobject的Destroy设计为虚函数."Obj.Free会不会不执行Obj本类的析构函数,而直接直接根类的Free"
    综合上面说的,的确会有这种情况,比如
    -你的类没有定义free.当你执行Tyourclass.free时,实际调用的是Tobject.free. 这时候,如果你的析构函数Destroy没有用override重写基类的Destroy的话,那这个时候调用的就是Tobject.Destroy;如果你的Destroy没有用override,那你必须自己再写个free方法来调用这个析构函数.
      

  9.   

    Obj.Free会不会不执行Obj本类的析构函数,而直接直接根类的Free 
    ===============
    这个问题是一个关于virtual/dynamic method的执行问题(constructor里面执行的时候例外)
      

  10.   

    楼上大哥的第一个解释我认为比较牵强
    可以这样:
    Try
      TObject(Obj).Free
    Finally
      Pointer(Obj):=Nil;
    end;"所以首先确保Pointer(Obj):=nil; 再去free "
    如果先赋值为Nil,而后Free发生异常怎么办?谁去负责把该块内存的使用标识置为可用?第二个还有个小问题:
    假设有一类MyClass,继承自TObject,且其内有Destroy,如果用MyClass.Free,则直接将其变为TObject类型后,Free,谁负责MyClass中的资源回收?
      

  11.   

    个人见解
    freeandnil遇到异常也是要抛出来的,你try了异常就被截了,难道在finally里再throw出来? 那还不如用个temp缓存下,先nil再free"如果先赋值为Nil,而后Free发生异常怎么办?谁去负责把该块内存的使用标识置为可用?"
    temp的生存期只到free函数结束.结束时候temp本身这个引用(可以理解为指针)就被回收了. 而free发生异常只可能是没有分配内存而要强制回收(没有create出实例),所以"谁去负责把该块内存的使用标识置为可用"这个没必要担心吧
    MyClass.Free调用的就是Tobject.free,Tobject.free再调用Tobject.Destroy
    所以,如果MyClass需要在析构时做自定义的处理,必须override一个Destroy.这样Tobject的Destroy实际就是调用了MyClass的Destroy. 这就是为什么delphi要设计Tobject的Destroy是个纯虚函数.举个例子:
    type
    Taaa = class(Tobject)
       destructor Destroy;override;
       end;destructor Taaa.Destroy;
    begin
    showmessage('aaa');
    end;你执行Taaa.free会执行showmessage('aaa');
    但如果不用override:
    Taaa = class(Tobject)
       destructor Destroy;
       end;Taaa.free就不会调用showmessage('aaa');
      

  12.   

    就一会没有来,楼都建得这么高了!我想对于你这个问题,你先要搞清楚内存空间、和指向内存空间的指针(地址,在对面对象的程序中经常用引用)区别和联系。对于
    procedure FreeAndNil(var Obj)函数来说,其参数类型是引用类型(也是通过传址的方式来实现的),而这个类型本身又是一个
    对象引用。Temp := TObject(Obj)只是提取Obj所指向的对象的引用到temp;Pointer(Obj) := nil就是把传进来的形参本身置空,而
    这个过程就会导致Obj对应的实参也会变为空(因为参数类型是引用类型嘛)!如:
    var
      obj: TObject;
    begin
      obj := TObject.Create;
      FreeAndNil(obj);//==>obj:=nil;
      if obj <> nil then
        ShowMessage('Obj不为空!');
    end;
    再看Temp.Free因为temp引用已经得到了参数所代表对象的引用,它再Free就可以调用到对象的析构函数了,从而对于达到释放内存
    的目的。
    至于你说的“结论是经过FreeAndNil的,if Self <>nil then  为永假式!从来就不执行Destroy~”,显然是不对的!这个是类和对象
    的区别和联系,这样讲吧,对于同一个类我们可以创建多个此类的对象。而我们再进行面向对象的编程中,实际上是针对类来编程,而
    不是对象,这点我想你不会不同意吧?!当你在写一个类TA的成员方法,而这个方法又来访问这个类的成员变量,如下示:
    type
      TA = class
      private
        Val: Integer;
      public
        function GetVal: integer;
        constructor Create(initVal: Integer);
      end;implementation{ TA }constructor TA.Create(initVal: Integer);
    begin
      Val :=  initVal;
    end;function TA.GetVal: integer;
    begin
      Result := Val;
    end;
    如果我们创建两个TA对象如下:
    a1 := TA.Create(1); a2 := TA.Create(3);那么你再分别调用a1,a2的getVal,那到底是多少呢?是1还是3
    我想你在设计这个类的时候时根本就不知道Val成员是多少,或者换一个角度来讲,你根本不关心Val是多少
    因为你相信Val会是正确(也就是GetVal能返回正确的值),更深一个层次来讲:你知道当TA的一个对象的值产生了以后,这个Val成员应该有一个
    自己的值,就算自己修改了Val,也不会影响其它的TA对象的Val。为了作了这一点,Delphi就必须保证在设计类时操作一个类的成员的时候必须
    是绑定到一个具体的对象上的! 这一点就是Self这个关键字的功能。可能说到这里,问题差不多就解决!
    当我们调用a1.getValue,其实是在调用function TA.GetVal: integer时实际上是返回了a1.Val成员,它等价与Self.Val,而调用
    a2.getValue实际上返回了a2.Val成员,它也等价与Self.Val。细心的你可能已经看出来了其实在a1.GetValue时,Self表示的是a1;
    而a2.getValue时,Self表示的是a2。也就是在执行类的成员方法时Self表示是 当前调用这个成员方法的对象的本身!!也许你可能说我是在猜
    想,那请看下面这个实例:
    type
      TA = class
      private
        Val: Integer;
      public
        function GetVal: integer;
        function GetSelfAddr: Integer;
        constructor Create(initVal: Integer);
      end;{ TA }constructor TA.Create(initVal: Integer);
    begin
      Val :=  initVal;
    end;function TA.GetVal: integer;
    begin
      Result := Val;
    end;function TA.GetSelfAddr: Integer;
    begin
      Result := Integer(Self);
    end;procedure TForm1.Button1Click(Sender: TObject);
    var
      a1, a2: Ta;
    begin
      a1 := TA.Create(1);
      a2 := TA.Create(3);  if Integer(a1) = a1.GetSelfAddr then
        ShowMessage('Now Self=a1')
      else
        ShowMessage('Now Self<>a1');  if Integer(a2) = a2.GetSelfAddr then
        ShowMessage('Now Self=a2')
      else
        ShowMessage('Now Self<>a2');  a1.Free;
      a2.Free;
    end;
    简单的实验一下,你会发现当a1:Ta再调用Ta.GetSelfAddr方法时,self==a1;当a2:Ta再调用Ta.GetSelfAddr方法时,self==a2;
    也就是Self是 动态关联到每个调用类的成员方法 具体对象的!
    所以“结论是经过FreeAndNil的,if Self <>nil then  为永假式!从来就不执行Destroy~ ”当然就不对了,因为用不同的对象在调用
    Free时,其实Self是不一样的!
      

  13.   

    当然是MyClass的成员了!因为Free是非虚拟方法,而它调用的实际是Destroy,而Destroy是虚拟方法呀(当然如果MyClass没有覆盖就是另外一回事了)!
    我要说的就是如果你的MyClass以一种特殊的方式去析构的话(覆盖了Destroy方法),即使你只有MyClass父类的引用,那么就是父类的Destroy释放方法,
    也会最终执行到你覆盖以后的MyClass的Destroy方法