Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Программирование на языке Delphi_3.doc
Скачиваний:
37
Добавлен:
28.03.2015
Размер:
1.71 Mб
Скачать

6.13. Получение интерфейса через другой интерфейс

Через интерфейсную переменную у объекта всегда можно запросить интерфейс другого типа. Для этого используется оператор as, например:

var

Intf: IInterface;

begin

...

with Intf as ITextReader do

Active := True;

...

end;

Если объект действительно поддерживает запрашиваемый интерфейс, то результатом является ссылка соответствующего типа. Если же объект не поддерживает интерфейс, то возникает исключительная ситуация EIntfCastError.

В действительности оператор as преобразуется компилятором в вызов метода QueryInterface:

var

Intf: IInterface;

IntfReader: ITextReader;

...

IntfReader := Intf as ITextReader; // Intf.QueryInterface(ITextReader, IntfReader);

Напомним, что метод QueryInterface описан в интерфейсе IInterface и попадает автоматически во все интерфейсы. Стандартная реализация этого метода находится в классе TInterfacedObject.

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

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

Подсчет ссылок на объект обеспечивают методы _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