Reputation: 475
Consider this short Delphi procedure:
procedure TfrmXQuery.FieldListFillFromDefault;
var
field_list: TStringList;
begin
try
if x <> '' then begin
field_list := TStringList.Create;
{do some stuff with field_list}
end;
finally
if field_list <> NIL then
begin
field_list.Free;
end;
end;
end;
When I run this in, Delphi 3, with x = '' so that field_list is never created,
field_list <> NIL
? NIL
? NIL
what is it? NIL
how do I know whether or not to Free
it? The Assigned
function does not tell me: if Assigned(an_object)
is equivalent to if an_object = NIL
Upvotes: 4
Views: 2369
Reputation: 475
Answers to the questions:
why is field_list <> NIL
?
Delphi does not initialize local objects. For more details see: Why doesn't object default to nil? and Are delphi variables initialized with a value by default?
are objects not initialized as NIL
? Global objects: Yes. Local objects: No.
if it's not NIL
what is it? Invalid pointers.
and if it's unassigned and not NIL
how do I know whether or not to Free
it? You need to reorganize your code (see below).
The Assigned
function does not tell me: if Assigned(an_object)
is equivalent to if an_object = NIL
. Assigned tests for a nil (unassigned) pointer or procedural variable. <--from Delphi 3 documentation.
Uninitialized local objects are not assigned NIL so Assigned(an_object)
returns TRUE if an_object is local and has never been used (assigning NIL is using the object).
Because objects local to a procedure are not initialized to NIL, I have revised the code from the question assigning all local objects that are local to NIL. I do these assignments at the very beginning of the routine so that Free will not error out if the local objects are never created. I've also shown the error tracking code that was left out of the original question:
procedure TfrmXQuery.FieldListFillFromDefault;
var
field_list: TStringList;
some_other_object: TAnotherObject;
begin
try
try
field_list := NIL;
some_other_object := NIL;
if x <> '' then begin
field_list := TStringList.Create;
{do some stuff with field_list}
end;
{...}
some_other_object := TSomeOtherObject.Create;
{...}
except
On E : Exception do begin
ErrorTrackingRoutine(unit_name, name, 'FieldListFillFromDefault', E.message);
end;
end;
finaly
field_list.Free;
some_other_object.Free;
end;
end;
The entire routine is protected by try...except
. If field_list
or some_other_object
are created it will be freed. They are assigned NIL at the very beginning so freeing them in the 'try...finally`
block will not raise an error even if there is a run time error before they are created.
Upvotes: -3
Reputation: 125620
The problem is that if x = ''
, the finally
happens anyway. Since field_list
is only initialized when x <> ''
, it's a random memory location before that point, because it's an uninitialized local variable. The random value allows the field_list.free
to be called, because it's not equal to nil
. (Delphi doesn't initialize local variables (those declared within a function or procedure).)
var
somevar: sometype;
begin
// at this point, somevar is just a chunk of memory that
// holds whatever happens to be in that chunk
somevar := nil; // now somevar = a specific value you can test
// other code
end;
You shouldn't have to test for <> nil
(as others have pointed out in comments) if you structure your code correctly.
procedure TfrmXQuery.FieldListFillFromDefault;
var
field_list : TStringList;
begin
if x <> '' then
begin
field_list := TStringList.Create;
try
{do some stuff with field_list}
finally
field_list.Free;
end;
end;
end;
(If you turn on hints and warnings, the compiler would have told you that field_list may not have been initialized
, which would have helped you solve this yourself.)
Upvotes: 8