У меня есть форма Delphi, которая обеспечивает функциональность объекта интерфейса, на которую другие части кода также получают ссылки через свойство, принадлежащее форме. Я не могу делегировать функциональность интерфейса дочернему объекту, потому что слишком большая часть этой функциональности обслуживается элементами управления / компонентами в форме. Я не могу использовать TAggregatedObject или TConolatedObject для привязки времени жизни интерфейсных объектов, передаваемых к форме, потому что класс TForm не наследуется от TinterfacedObject, а Delphi не поддерживает множественное наследование, поэтому я не могу смешивать TInterfacedObject в цепочке наследования . Эта ситуация может привести к нарушениям доступа, если форма уничтожается, в то время как какой-либо другой код содержит одну из ссылок на интерфейс, переданных этой формой. Кто-нибудь может придумать хорошее решение этой проблемы?
Безопасный способ в Delphi для формы распространять объекты интерфейса, привязанные к его времени жизни?
Ответы (2)
Вы можете делегировать интерфейс дочернему объекту, просто сделав так, чтобы этот объект содержал внутренний указатель на форму, чтобы он мог при необходимости обращаться к элементам управления формы, ничем не отличается от того, что вы уже делаете прямо сейчас.
Вы можете использовать TAggregateObject
или TContainedObject
для своих нужд. Они не требуют, чтобы Форма производилась от TInterfacedObject
. Все, что им требуется, это указатель интерфейса IInterface
, а TComponent
является производным от IInterface
(и отменяет _AddRef()
и _Release()
, чтобы отключить подсчет ссылок), поэтому вы можете передать саму форму (являющуюся потомком TComponent
) в качестве требуемого указателя IInterface
.
Остается единственная проблема - закрытие формы, в то время как активные ссылки на интерфейс удерживаются другим кодом. Самое простое решение - либо 1) переписать этот код, чтобы не удерживать эти ссылки, пока форма закрывается, или 2) не позволять форме закрыться до тех пор, пока эти ссылки не будут освобождены.
Примечание: это будет работать, только если ваш потребитель также является производным от TComponent.
Чтобы избежать мертвых ссылок, вы можете запросить IInterfaceComponentReference
(доступный на каждом TComponent) из вашей формы, вызвать GetComponent
в этом интерфейсе и присоединиться к FreeNotification
возвращенного компонента / формы.
Теперь происходит следующее: когда форма уничтожается, она уведомляет всех «листинговых», что она собирается уничтожить себя, вызывая метод Notification
на потребителе с самим собой (формой) как AComponent
и opRemove
как операция. Таким образом, вы можете обнулить ссылку на интерфейс. Но имейте в виду, что ссылки на объекты и ссылки на интерфейсы не должны быть равными. Также не забудьте позвонить RemoveFreeNotification
, когда вам больше не нужно Уведомление, чтобы избежать ненужных вызовов.
TSomeConsumer = class(TComponent)
private
FInterfaceToAService: ISomeInterface;
protected
procedure Notification(AComponent: TComponent; Operation: TOperation); override;
public
procedure SetService(const Value: ISomeInterface);
end;
procedure TSomeConsumer.Notification(AComponent: TComponent; Operation: TOperation);
begin
inherited;
if (Operation = opRemove) and (AComponent = TObject(FInterfaceToAService)) then
SetService(nil); // Takes care of niling the interface as well.
end;
procedure TSomeConsumer.SetService(const Value: ISomeInterface);
var
comRef: IInterfaceComponentReference;
begin
if Supports(FInterfaceToAService, IInterfaceComponentReference, comRef) then
comRef.GetComponent.RemoveFreeNotification(self);
FInterfaceToAService := Value;
if Supports(FInterfaceToAService, IInterfaceComponentReference, comRef) then
comRef.GetComponent.FreeNotification(self);
end;
QueryInterface
, которая может возвращать новый интерфейсный объект при каждом вызове. б) Во-вторых, вы можете делегировать интерфейс свойству (Win32), которое может снова создавать новый объект каждый раз. c) Реализация TObject.GetInterface
добавляет к адресу interfaceOffset. (Но эти внутренние механизмы, к сожалению, недоступны нам.) Недавно представленный (Delphi 2010) интерфейс для литья объектов в основном выполняется путем запроса интерфейса маркера, который возвращает адрес входа объекта.
- person Tobias R; 11.10.2011
FreeNotification
регистрацию. Использование ссылки на интерфейс вместо TComponent
- это немного больше накладных расходов.
- person Tobias R; 11.10.2011