Механизм подсчета ссылок

Механизм подсчета ссылок на объект предназначен для автоматического уничтожения неиспользуемых объектов. Неиспользуемым считается объект, на который не ссылается ни одна интерфейсная переменная.

Подсчет ссылок на объект обеспечивают методы _AddRef и _Release интерфейса IInterface. При копировании значения интерфейсной переменной вызывается метод _AddRef, а при уничтожении интерфейсной переменной - метод _Release. Вызовы этих методов генерируются компилятором автоматически:

var
  Intf, Copy: IInterface;
begin
  ...
  Copy := Intf; // Copy._Release; Intf._AddRef;
  Intf := nil;  // Intf._Release; 
end;            // Copy._Release

Стандартная реализация методов _AddRef и _Release находится в классе TInterfacedObject. Она достаточно проста и вы легко разберетесь с ней, читая комментарии в исходном тексте.

type
  TInterfacedObject = class(TObject, IInterface)
    ...
    FRefCount: Integer;                       // Счетчик ссылок
    function _AddRef: Integer; stdcall;
    function _Release: Integer; stdcall;
    ...
  end;

function TInterfacedObject._AddRef: Integer;
begin
  Result := InterlockedIncrement(FRefCount);  // Увеличение счетчика 
                                              //ссылок
end;

function TInterfacedObject._Release: Integer;
begin
  Result := InterlockedDecrement(FRefCount);  // Уменьшение счетчика
                                              // ссылок
  if Result = 0 then                          // Если ссылок больше
                                              // нет, то
    Destroy;                                  // уничтожение объекта
end;

Заметим, что функции InterlockedIncrement и InterlockedDecrement просто увеличивают значение целочисленной переменной на единицу. В отличие от обычного оператора сложения, они обеспечивают атомарное изменение значения переменной, что очень важно для правильной работы распараллеленных (многопоточных) программ.

Приведенную выше реализацию методов _AddRef и _Release автоматически получают все наследники класса TInterfacedObject, в том числе и классы TTextReader, TDelimitedReader и TFixedReader. Поэтому неиспользуемые объекты классов TDelimitedReader и TFixedReader тоже автоматически уничтожаются при работе с ними через интерфейсные переменные:

var
  Obj: TDelimitedReader;
  Intf, Copy: ITextReader;
begin
  Obj := TDelimitedReader.Create('MyData.del', ';'); 
  Intf := Obj;   // Obj._AddRef -> Obj.FRefCount = 1
  Copy := Intf;  // Obj._AddRef -> Obj.FRefCount = 2
  ...
  Intf := nil;   // Obj._Release -> Obj.FRefCount = 1
  Copy := nil;   // Obj._Release -> Obj.FRefCount = 0 -> Obj.Destroy
  Obj.Free;      // Ошибка! Объект уже уничтожен и переменная 
                 //Obj указывает в никуда
end;

Обратите внимание, что объектные переменные не учитываются при подсчете ссылок. Поэтому мы настоятельно рекомендуем избегать смешивания интерфейсных и объектных переменных. Если вы планируете использовать объект через интерфейс, то лучше всего результат работы конструктора сразу присвоить интерфейсной переменной:

var
  Intf: ITextReader;
begin
  Intf := TDelimitedReader.Create('MyData.del', ';'); //FRefCount = 1
  ...
  Intf := nil;                              // FRefCount=0->Destroy
end;

Если интерфейс является входным параметром подпрограммы, то при вызове подпрограммы создается копия интерфейсной переменной с вызовом метода _AddRef:

procedure LoadItems(R: ITextReader);
begin
...
end;

var
  Reader: ITextReader;
begin
  ...
  LoadItems(Reader); // Создается копия переменной Reader 
                    //и вызывается Reader._AddRef
end;

Копия не создается, если входной параметр описан с ключевым словом const:

procedure LoadItems(const R: ITextReader);
begin
...
end;

var
  Reader: ITextRedaer;
begin
  ...
  LoadItems(Reader); // Копия не создается, метод 
                     //_AddRef не вызывается 
end;

Интерфейсная переменная уничтожается при выходе из области действия переменной, а это значит, что у нее автоматически вызывается метод _Release:

var
  Intf: ITextRedaer;
begin
  Intf := TDelimitedReader.Create('MyData.del', ';');
  ...
end; // Intf._Release

 

Яндекс цитирования