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

con*_*tor 5 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.
Run Code Online (Sandbox Code Playgroud)

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

有人可以帮忙赐教吗?

Cod*_*aos 15

MyObject是与该领域不同的变量FMyObject.而你只是nil在这个领域FMyObject.

FreeAndNil释放到指向的对象,以及nil传入的变量.它不会神奇地发现,并且nil所有其他变量都指向您释放的对象.

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

object(FMyObject).Free();
FMyObject=nil;
Run Code Online (Sandbox Code Playgroud)

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

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


哦,我刚注意到你隐藏了原来的Free方法?那太疯狂了.FreeAndNil仍然使用原件Free.在你的例子中,这并没有打你,因为你调用的Free是静态类型的变量TBigObject而不是FreeAndNil.但它是灾难的收据.

您应该改写析构函数Destroy.


Ond*_*lle 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;
Run Code Online (Sandbox Code Playgroud)

  • @rudy经常这是个坏主意.使用FreeAndNil可以更容易地找到错误.不使用它不. (7认同)
  • 啊,旧的'FreeAndNil`与`TObject.Free`讨论......这件事可以永远地去ehehehehe :-) (4认同)

Dav*_*nan 8

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

i := 1;
j := i;
i := 0;
Writeln(j);//outputs 1
Run Code Online (Sandbox Code Playgroud)

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

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

obj1 := TObject.Create;
obj2 := obj1;
obj1.Free;//these two lines are
obj1 := nil;//equivalent to FreeAndNil
//but obj2 still refers to the destroyed object
Run Code Online (Sandbox Code Playgroud)

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