Delphi:为什么FreeAndNil *真的没有我的对象?

时间:2011-11-07 12:18:26

标签: delphi memory-management

我想将对象A传递给第二个对象B,让B做一些处理,最后释放A以防不再需要它。下面给出了一个淡化版本。

program Project6;
{$APPTYPE CONSOLE}
uses
  SysUtils;
type
  TMyObject = class(TObject)
  public
    FField1:  string;
    FField2:  string;
  end;
  TBigObject = class(TObject)
  public
    FMyObject:  TMyObject;
    procedure Bind(var MyObject:  TMyObject);
    procedure Free();
  end;
procedure TBigObject.Bind(var MyObject: TMyObject);
begin
  FMyObject := MyObject;
end;
procedure TBigObject.Free;
begin
  FreeAndNil(FMyObject);
  Destroy();
end;
var
  MyObject:   TMyObject;
  BigObject:  TBigObject;
begin
  try
    MyObject := TMyObject.Create();
    BigObject := TBigObject.Create();
    BigObject.Bind(MyObject);
    BigObject.Free();
    if (Assigned(MyObject)) then begin
      WriteLn('Set MyObject free!');
      MyObject.Free();
    end;
    ReadLn;
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
end.

(不要介意可怕的设计。)现在,我不明白为什么FreeAndNil实际上释放MyObject,但Assigned(MyObject)被评估为true(在MyObject.Free()给出AV)。

有人可以帮助启发我吗?

4 个答案:

答案 0 :(得分:15)

MyObject是与字段FMyObject不同的变量。而且你只是nil字段FMyObject

FreeAndNil释放指向的对象,nil是你传入的变量。它不会神奇地发现和nil指向你释放的对象的所有其他变量。

FreeAndNil(FMyObject);做同样的事情:

object(FMyObject).Free();
FMyObject=nil;

(从技术上讲,这不完全正确,由于无类型的var参数,对对象的强制转换是重新解释,但这与此无关)

这显然只会修改FMyObject而不是MyObject


哦,我刚刚注意到你隐藏了原来的Free方法?那太疯狂了。 FreeAndNil仍然使用原始Free。在您的示例中,您没有遇到这种情况,因为您对静态类型为Free而非TBigObject的变量调用了FreeAndNil。但这是灾难收据。

您应该覆盖析构函数Destroy

答案 1 :(得分:14)

原因很简单,你只需要一个参考而不是另一个参考。考虑这个例子:

var
  Obj1, Obj2: TObject;
begin
  Obj1 := TObject.Create;
  Obj2 := Obj1;
  FreeAndNil(Obj1);
  // Obj1 is released and nil, Obj2 is non-nil but now points to undefined memory
  // ie. accessing it will cause access violations
end;

答案 2 :(得分:8)

您有两个对象的引用副本,但只将其中一个设置为nil。您的代码等同于:

i := 1;
j := i;
i := 0;
Writeln(j);//outputs 1

我在这个例子中使用整数,因为我确信你熟悉它们是如何工作的。对象引用,实际上只是指针,行为方式完全相同。

根据对象引用来铸造示例使它看起来像这样:

obj1 := TObject.Create;
obj2 := obj1;
obj1.Free;//these two lines are
obj1 := nil;//equivalent to FreeAndNil
//but obj2 still refers to the destroyed object

旁白:你永远不应该直接调用Destroy,也不要声明一个名为Free的方法。而是覆盖Destroy并调用TObject中定义的静态Free,或者实际上是FreeAndNil。

答案 3 :(得分:1)

您的代码中有一些特殊之处。

首先,你不应该重写Free,你应该覆盖你班级的虚拟析构函数(Destroy)。

但ISTM认为BigObject不是MyObject的所有者,因此BigObject根本不应该尝试释放它。

正如CodeInChaos已经说过的,FreeAndNil只释放一个变量,在本例中是FMyObject字段。无论如何都不需要FreeAndNil,因为在释放对象后什么都不会发生。

已分配,不能用于检查对象是否已被释放。它只能检查nil,而FreeAndNil只将一个引用设置为nil,而不是对象本身(这是不可能的)。

因此,您的程序设计应该是一个对象只有在没有任何东西可以访问它的情况下才能被释放。