14.4.10 Managed types and reference counts

Some types (Unicodestring, Ansistring, interfaces, dynamic arrays) are treated somewhat specially by the compiler: the data has a reference count which is increased or decreased depending on how many reference to the data exists.

The qualifiers for parameters in function or procedure calls influence what happens to the reference count of the managed types:

Remark The function result is internally treated as a var parameter to the function, and the same rules as for var parameters apply.

The following example demonstrates the dangers:

{$mode objfpc}

Type
  ITest = Interface
    Procedure DoTest(ACount : Integer);
  end;

  TTest = Class(TInterfacedObject,ITest)
    Procedure DoTest(ACount : Integer);
    Destructor destroy; override;
  end;

Destructor TTest.Destroy;

begin
  Writeln('Destroy called');
end;

Procedure TTest.DoTest(ACount : Integer);

begin
  Writeln('Test ',ACount,' : ref count: ',RefCount);
end;

procedure DoIt1(x: ITest; ACount : Integer);

begin
  // Reference count is increased
  x.DoTest(ACount);
  // And decreased
end;

procedure DoIt2(const x: ITest; ACount : Integer);

begin
  // No change to reference count.
  x.DoTest(ACount);
end;

Procedure Test1;

var
  y: ITest;
begin
  y := TTest.Create;
  // Ref. count is 1 at this point.
  y.DoTest(1);
  // Calling DoIT will increase reference count and decrease on exit.
  DoIt1(y,2);
                                                                            

                                                                            
  // Reference count is still one.
  y.DoTest(3);
end;

Procedure Test2;

var
  Y : TTest;
begin
  Y := TTest.Create; // no count on the object yet
  // Ref. count is 0 at this point.
  y.DoTest(3);
  // Ref count will remain zero.
  DoIt2(y,4);
  Y.DoTest(5);
  Y.Free;
end;

Procedure Test3;

var
  Y : TTest;
begin
  Y := TTest.Create; // no count on the object yet
  // Ref. count is 0 at this point.
  y.DoTest(6);
  // Ref count will remain zero.
  DoIt1(y,7);
  y.DoTest(8);
end;


begin
  Test1;
  Test2;
  Test3;
end.

The output of this example is:

Test 1 : ref count: 1
Test 2 : ref count: 2
Test 3 : ref count: 1
Destroy called
Test 3 : ref count: 0
Test 4 : ref count: 0
Test 5 : ref count: 0
Destroy called
Test 6 : ref count: 0
Test 7 : ref count: 1
Destroy called
Test 8 : ref count: 0

As can be seen, in test3, the reference count is decreased from 1 to 0 at the end of the DoIt call, causing the instance to be freed before the call returns.

The following small program demonstrates the reference counts used in strings:

{$mode objfpc}
{$H+}

Procedure ByVar(Var S : string);

begin
  Writeln('By var, ref count : ',StringRefCount(S));
end;

Procedure ByConst(Const S : string);

begin
  Writeln('Const, ref count : ',StringRefCount(S));
end;

Procedure ByVal(S : string);

begin
  Writeln('Value, ref count : ',StringRefCount(S));
end;

Function FunctionResult(Var S : String) : String;

begin
  Writeln('Function argument, ref count : ',StringRefCount(S));
  Writeln('Function result, ref count : ',StringRefCount(Result));
end;

Var
  S,T : String;

begin
  S:='Some string';
  Writeln('Constant       : ',StringRefCount(S));
  UniqueString(S);
  Writeln('Unique         : ',StringRefCount(S));
  T:=S;
  Writeln('After Assign   : ',StringRefCount(S));
  ByVar(S);
  ByConst(S);
  ByVal(S);
  UniqueString(S);
  T:=FunctionResult(S);
  Writeln('After function : ',StringRefCount(S));
end.