Search code examples
delphidelphi-3

Delphi TStringList Free Causes Exception


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,

  1. why is field_list <> NIL?
  2. are objects not initialized as NIL?
  3. if it's not NIL what is it?
  4. and if it's unassigned and not 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

Solution

  • Answers to the questions:

    1. 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?

    2. are objects not initialized as NIL? Global objects: Yes. Local objects: No.

    3. if it's not NIL what is it? Invalid pointers.

    4. 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.