14.5 Управляение типами со счетчиком ссылок

Вверх  Предыдущий  Следующий

Некоторые типы (UnicodeString, AnsiString, интерфейсы, динамические массивы) обрабатываются особым образом: данные этих типов имеют счетчик ссылок, который увеличивается или уменьшается в зависимости от того, как много существует ссылок на эти данные.

Квалификаторы параметров вызова функций (или процедур) влияют на счетчик управляющий ссылками на типы:

ничего (без квалификатора) (передача по значению): счетчик ссылок на параметр увеличивается на входе и уменьшается на выходе.

out: при вызове подпрограммы счетчик ссылок на значение  уменьшается на 1, а переменной присваивается пустое значение (как правило Nil, но на эту деталь реализации не следует полагаться).

var со счетчиком ссылок ничего не происходит. Передается ссылка на исходную переменную, и изменение или чтение параметра имеет точно такой же эффект, как изменение/чтение исходной переменной.

const этот случай немного сложнее.Со счетчиком ссылок ничто не происходит, потому что здесь вы можете передать не-значение. Например, вы можете передать класс реализующий интерфейс, а не сам интерфейс вызывающий класс, и класс может быть неожиданно освобожден.

Следующий пример демонстрирует эту опасность:

{$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');

end;

 

Procedure TTest.DoTest(ACount : Integer);

begin

Writeln('Тестируем ',ACount,' : счётчик ссылок: ',RefCount);

end;

 

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

begin

// Счетчик ссылок увеличивается

x.DoTest(ACount);

// И уменьшается

end;

 

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

begin

// Счетчик ссылок не меняется.

x.DoTest(ACount);

end;

 

Procedure Test1;

var

y: ITest;

begin

y := TTest.Create;

..// Счётчик ссылок в этот момент равен 1.

y.DoTest(1);

// При входе в DoIT счётчик ссылок увеличивается и уменьшается при выходе.

DoIt1(y,2);

// Ещё один счетчик ссылок.

y.DoTest(3);

end;

 

Procedure Test2;

var

Y : TTest;

begin

Y := TTest.Create; // На объект еще нет счетчика ссылок

// В этой точке счётчик ссылок равен 0.

y.DoTest(3);

// Счетчик ссылок остаётся нулевым.

DoIt2(y,4);

Y.DoTest(5);

Y.Free;

end;

 

Procedure Test3;

var

Y : TTest;

begin

Y := TTest.Create; // На объект еще нет счетчика ссылок

// В этой точке счётчик ссылок равен 0.

y.DoTest(6);

// Счетчик ссылок остаётся нулевым.

DoIt1(y,7);

y.DoTest(8);

end;

 

begin

Test1;

Test2;

Test3;

end.

Этот пример выведет:

Тестируем 1 : счётчик ссылок: 1

Тестируем 2 : счётчик ссылок: 2

Тестируем 3 : счётчик ссылок: 1

Вызывается Destroy

Тестируем 3 : счётчик ссылок: 0

Тестируем 4 : счётчик ссылок: 0

Тестируем 5 : счётчик ссылок: 0

Вызывается Destroy

Тестируем 6 : счётчик ссылок: 0

Тестируем 7 : счётчик ссылок: 1

Вызывается Destroy

Тестируем 8 : счётчик ссылок: 0

Как можно видеть, в test3, счетчик ссылок уменьшается с 1 до 0 в конце вызова Doit, в результате экземпляр будет освобожден до возвращения из подпрограммы.

Следующая небольшая программа демонстрирует счётчик ссылок, используемый в строках: