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

I want to pass object A to second object B, ask B to do some processing, and finally free A if it is no longer needed. Below is the version 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. 

(Don't mind the awful design.) Now I don’t understand why FreeAndNil really frees MyObject, but Assigned(MyObject) evaluates to true (gives AV in MyObject.Free() ).

Did anyone help me enlighten?

+4
source share
4 answers

The reason is simple, you have one link, but not another. 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; 
+13
source

MyObject is another variable from the FMyObject field. And you are only nil in the FMyObject field.

FreeAndNil frees the object that it points to, and nil the variable you passed. It does not magically detect, and nil all other variables point to the freed object.

FreeAndNil(FMyObject); does the same thing:

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

(Technically, this is not entirely correct; casting an object to an object is reinterpreted due to the untyped var parameter, but it does not matter here)

And this, obviously, only modifies FMyObject , not MyObject


Oh, I just noticed that you are hiding the original Free method? This is madness. FreeAndNil still uses the original Free . This will not hit you in your example, because you are calling Free on a variable with the static type TBigObject , not FreeAndNil . But this is a receipt for the disaster.

Instead, you should override the Destroy destructor.

+15
source

You have two copies of the object reference, but they only set one of them to zero. Your code is equivalent to this:

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

I use integers in this example because I'm sure you are familiar with how they work. References to objects that are actually just pointers behave exactly the same.

Providing an example with respect to object references makes it like this:

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

Beyond this: You should never call Destroy directly and never declare a Free method. Instead, override Destroy and call static Free on the TObject, or indeed FreeAndNil.

+8
source

There are several features in your code.

First, you must not rewrite Free; you must override the virtual class Destroy of your class.

But the ISTM is that BigObject does not own MyObject, so BigObject should not even try to free it.

As mentioned in CodeInChaos, FreeAndNil only frees the variable one , in this case the FMyObject field. FreeAndNil is not required, because after the release of the object nothing can happen.

Assigned cannot be used to check if an object has already been freed. It can only check for nil, and FreeAndNil sets only one link to zero, not the object itself (this is not possible).

Your program design should be such that an object can and will be freed only if it no longer refers to it.

+1
source

Source: https://habr.com/ru/post/924154/


All Articles