Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Delphi: why doesn't FreeAndNil *really* nil my object?

I want to pass an object A to a second object B, have B do some processing and finally release A in case it's not needed anymore. A watered down version is given below.

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.

(Never mind the awful design.) Now, what I don't understand is why FreeAndNil actually does free MyObject, yet Assigned(MyObject) is evaluated to true (giving an AV at MyObject.Free()).

Could someone please help enlighten me?

like image 219
conciliator Avatar asked Nov 07 '11 12:11

conciliator


2 Answers

MyObject is a different variable from the field FMyObject. And you're only niling the field FMyObject.

FreeAndNil frees to object pointed to, and nils the variable you passed in. It doesn't magically discover and nil all other variables that point to the object you freed.

FreeAndNil(FMyObject); does the same thing as:

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

(Technically this is not entirely correct, the cast to object is a reinterpret cast due to the untyped var parameter, but that's not relevant here)

And that obviously only modifies FMyObject and not MyObject


Oh I just noticed that you're hiding the original Free method? That's insane. FreeAndNil still uses the original Free. That doesn't hit you in your example because you call Free on a variable with the static type TBigObject and not FreeAndNil. But it's a receipt for disaster.

You should instead override the destructor Destroy.

like image 51
CodesInChaos Avatar answered Sep 28 '22 14:09

CodesInChaos


The reason is simple, you nil one reference but not the other. Consider this example:

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;
like image 23
Ondrej Kelle Avatar answered Sep 28 '22 12:09

Ondrej Kelle