и ссылки на длинные строки,
Ссылки на интерфейсы, как и ссылки на длинные строки, подсчитываются. Каждый раз, когда вы создаете копию переменной, содержащей интерфейсную ссылку (непосредственным присваиванием или при передаче параметра процедуре), вызывается метод
_AddRef объекта, который увеличивает значение счетчика ссылок. При каждом уничтожении ссылки на интерфейс (непосредственным присваиванием или при выходе за пределы области видимости) вызывается метод
_Release
объекта, который уменьшает значение счетчика ссылок. Когда значение счетчика достигает 0, объект удаляет себя. «Обычные» объектные ссылки никак не влияют на процесс подсчета ссылок.
Данная схема прекрасно работает — если вы взаимодействуете с объектом только с помощью интерфейсных ссылок. Например, для следующего фрагмента:
type IFoo = interface procedure Foo; end; TFoo = class (TObject, IFoo) procedure Foo; end;
procedure TFoo.Foo; begin end;
prcedure Bar(InterfaceReference: IFoo); begin end;
begin Bar(TFoo.Create); end.
ALIGN="JUSTIFY">объект
TFoo, созданный вызовом Bar, автоматически уничтожается при выходе из Bar. Но давайте рассмотрим слегка измененный сценарий, в ором интерфейсные ссылки смешиваются со ссылками на объекты:
var ObjectReference: TFoo;
begin ObjectReference := TFoo.Create; try Bar(ObjectReference); finally ObjectReference.Free; end; end.
Проблема заключается в том, что присваивание
ObjectReference := TFoo.Create не влияет на счетчик ссылок объекта. Свойство
RefCount
продолжает оставаться равным 0, как и при создании объекта. Тем не менее при вызове процедуры
Bar происходит неявное присваивание ее параметру
InterfaceReference. При этом генерируется вызов
_AddRef, в результате которого
RefCount становится равным 1. При выходе из
Bar заканчивается область видимости параметра
InterfaceReference, поэтому генерируется вызов
_Release. В результате
RefCount снова обнуляется, что приводит к уничтожению объекта. Ссылка
Object Reference становится недействительной! При следующем обращении к ней (в нашем случае — при вызове
Free) возникает GPF.
Подобный сценарий выглядит достаточно хитроумно, но он неплохо демонстрирует те проблемы, с которыми вы столкнетесь при попытке добавить интерфейсы в старый код. Но даже в некоторых новых программах бывает удобно работать с объектами, смешивая интерфейсные и объектные ссылки (например, при работе с
TList
интерфейсные ссылки иногда оказывают неоценимую помощь).
В подобных случаях следует принудительно увеличить счетчик ссылок объекта на 1 еще до получения первой интерфейсной ссылки на него. Например, класс
TAbstractView из следующего раздела содержит следующий обработчик
OnCreate:
procedure TAbstractView.FormCreate (Sender: TObject); begin inherited; _AddRef; // теперь Self можно передавать // в качестве интерфейсной ссылки end;
Явный вызов
_AddRef означает, что при создании первой интерфейсной ссылки
RefCount
увеличится до 2 и в дальнейшем никогда не станет равным 0. Следовательно, объект никогда сам не уничтожится и не разрушит ваших объектных ссылок; он будет жить до тех пор, пока вы не освободите его с помощью
Free.
Разумеется, явный вызов
_AddRef
необходим лишь при смешивании объектных и интерфейсных ссылок. Если вы собираетесь взаимодействовать с объектом только через интерфейсные ссылки, к явным вызовам
_AddRef
следует относиться с большой осторожностью — вы можете нарушить всю систему подсчета ссылок и ваш объект не будет уничтожаться. И наоборот, при работе с «чисто интерфейсным» объектом никогда не создавайте объектных ссылок на него, иначе они станут недействительными после того, как счетчик интерфейсных ссылок упадет до 0 и объект самоуничтожится. Одна из простейших мер предосторожности состоит в том, чтобы поместить все интерфейсные методы в секцию
protected — они останутся доступными через интерфейс, но раз вы не сможете обратиться к ним через объектные ссылки, исчезнет и повод эти ссылки создавать.
Содержание раздела