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, в результате экземпляр будет освобожден до возвращения из подпрограммы. Следующая небольшая программа демонстрирует счётчик ссылок, используемый в строках: |