Search code examples
delphimemoryobject-reference

What does the Free() method actually do internally and how does it handle object references?


Question

In the code below a new object of type TStringList is created and passed to a procedure which is using that object. By passing the object to the method ProcToFillStringList a new object reference is created by coping the reference. My questions regarding this code are:

  1. What happens to the object reference stored in the parameter SList after the method returns? Does it remove the reference to the object from the stack?

  2. What does the Free() method actually do internally? Does it remove all references to the object from the stack or does it remove the object itself? Which references are removed?

  3. Do object references (not the object itself) get removed from stack automatically when a method returns?

  4. Would it be better to pass the reference byref?

Code

var
  SL: TStringList; // first object reference
begin
  SL := TStringList.Create; // creating object
  try
    ProcToFillStringList(SL);
  finally
    SL.Free; // -> what gets 'freed' here? the object? the references? both?
  end;
end;

procedure ProcToFillStringList(const SList: TStrings); // second object reference
  SList.Add('x'); // not calling Free -> does the reference get removed?
end;

Solution

  • Here is code of Free method on newer versions of Delphi:

    procedure TObject.Free;
    begin
    // under ARC, this method isn't actually called since the compiler translates
    // the call to be a mere nil assignment to the instance variable, which then calls _InstClear
    {$IFNDEF AUTOREFCOUNT}
      if Self <> nil then
        Destroy;
    {$ENDIF}
    end;
    

    There are two different cases. When compiled to environment with automatic reference counting (that is iOS), Free doesn't work at all, objects are freed only when the last reference to them is removed (but as said in comments to code above, compiler changes your SL.Free to SL:=nil, so if it was the last reference to object, it will be freed and SL is really set to nil.

    But in all other platforms objects are not reference counted. When calling Free, object memory is freed, but your variable is not set automatically to nil (not saying about another variables pointing to the same object), that's just impossible with syntax like this. Any method of object can't change variable it's called from. That's why you write SL := TStringList.Create instead of SL.Create. In first case you get new memory address where object is created and assign SL to it. In second SL is not initialized and can point anywhere, so there is no way to create object exactly there.

    So, to answer your questions:

    1. Object reference in local procedure is removed when it goes beyond the scope. But if you use const or var argument, it is not created in the first place. Actually, you're using the same reference SL here.

    2. In iOS Free does nothing, object will be destroyed automatically when SL variable goes beyond the scope. In other platforms, Free destroys object and doesn't affect other references at all.

    3. Yes, they do.

    4. Use that modifier which describes your situation best. Const will tell compiler and people working with your code (including yourself) that argument won't be changed in procedure, compiler may pass it by value (for objects less than pointer) or by reference, but no matter what it chooses, refcount will never be increased, so from this point of view, you can think that you use exactly the same object, like it was passed by reference.

    Using Var (by reference) you can accidentally change the variable you passed to procedure and this makes your intentions unclear, so use it only when you really want to change this variable and Const otherwise.