3

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;
MUG4N
  • 19,377
  • 11
  • 56
  • 83
  • 1
    Have you looked at the source of TObject.Free in System.Pas and of TStringList.Free in Classes.Pas? – MartynA May 14 '16 at 12:07
  • http://stackoverflow.com/questions/8548843/why-should-i-not-use-if-assigned-before-using-or-freeing-things/8550628#8550628 – David Heffernan May 14 '16 at 15:49
  • 3
    You keel talking about removing references from the stack but that doesn't make any sense. I'm not sure your understanding of what the stack is is accurate. – David Heffernan May 14 '16 at 16:13
  • @David: Maybe you are right. Do you know where I could get a better understanding of ther underlying concept? – MUG4N May 15 '16 at 11:41
  • Not sure. The idea that everything lives on the stack will take some fixing. Objects live on the heap. References can be on stack or heap. – David Heffernan May 15 '16 at 11:57

3 Answers3

9

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.

Yuriy Afanasenkov
  • 1,440
  • 8
  • 12
  • const parameters are passed by reference only if they are larger than a pointer. So your answer for item 4 is wrong. – David Heffernan May 14 '16 at 16:15
  • @DavidHeffernan Do I understand correctly that const parameters, even passed by value don't increase refcount, be it string, interface anywhere or object in ARC environment? Anyway, I'll correct answer for item 4 now. Just want to make it clear in this aspect, too. – Yuriy Afanasenkov May 14 '16 at 16:22
  • const params don't have ref count increased because the compiler knows they cannot be modified. Whether the param is passed by value or by ref is a disjoint concern – David Heffernan May 14 '16 at 16:44
1

In the documentation of embarcadero is written

System::TObject::Free automatically calls the destructor if the object reference is not nil

It means in your case that the object SL is cleared at the point you called SL.Free. A object inherited from TObject does not know how many references are alive to that instance. Only the pointer to the address of the instance of SL is passed to the function call ProcToFillStringList. The instance is not informed about the new reference.

If you want to handle reference counting have a look at TInterfacedObject and the 3 Methods

QueryInterface
_AddRef
_Release
FlorianSchunke
  • 571
  • 5
  • 15
0

a new object reference is created by coping the reference

New reference const SList is just non-changeable pointer to the object. It will be removed from the stack if it lives there (in this case parameter is passed through register).

Free doesn't clear any references. It just destructs an object, frees it's memory. There is 'FreeAndNil' routine that frees object and makes one reference nil. Other references still exist.

MBo
  • 77,366
  • 5
  • 53
  • 86