Borland introduced the FreeAndNil procedure in Delphi 5. It frees an object while ensuring that
the object reference passed in will be nil
afterward, even if freeing the object results in an exception.
Uses
The only reason to set a variable to nil
(or to any value, for that matter) is if some later portion of
the program relies on the variable having that value. Otherwise, the
assignment statement is just wasted code.
The Delphi compiler does not perform the kind of interprocedural
analysis necessary to recognize and hint about the ineffectiveness of
calling FreeAndNil on a variable that will never be accessed
again. It always assumes that a var parameter will be used somewhere else
that it cannot detect.
The parameter
FreeAndNil takes an untyped var parameter. Why not declare it as TObject?
If it had a type, then the compiler’s type checking would interfere
with the convenience of calling the function. If the variable passed to a
type-explicit FreeAndNil were not declared as
TObject, there would be a compiler error: “Types of actual and formal var parameters
must be identical.”
Since FreeAndNil is supposed to work on any
TObject descendant, it cannot take a typed var parameter.
The trade-off is that with an untyped parameter, FreeAndNil
will also accept non-TObject types. There is nothing to
prevent you from accidentally passing a generic
GetMem-allocated pointer to FreeAndNil,
although you’ll get get an error at run time. (For regular
pointers, you can write your own FreeMemAndNil procedure or
use the one provided in the JCL.)
The name
Some argue that FreeAndNil should actually be named
NilAndFree because that is the order of operations in
Borland’s implementation. The function stores the object reference
into a local variable, sets the original argument to nil, and then calls Free on the
local. It does that to ensure that the argument is nil even in the event of an exception while
freeing the object. To implement the function in the order that its name
suggests, a try-finally block could have been used, like so:
Listing 1
An alternative FreeAndNil implementation
procedure FreeAndNil(var Obj);
begin
try
TObject(Obj).Free;
finally
TObject(Obj) := nil;
end;
end;
There is nothing wrong with that code, except that Borland’s
implementation provides nearly identical results without the overhead of
setting up and tearing down an exception frame. The difference in results
occurs when the actual argument passed to the function is a global
variable or otherwise shared among multiple sections of a program. When
that is the case, if code invoked by the object’s destructor
accesses the shared variable, it will find that the variable’s
value is already nil when using
Borland’s FreeAndNil, but it will find the variable
unchanged when using the alternative implementation in Listing 1.