Безопасный способ в Delphi для Form распространять объекты интерфейса, связанные с его временем жизни? - PullRequest
7 голосов
/ 11 октября 2011

У меня есть Delphi Form, которая обеспечивает функциональность за интерфейсным объектом, на который другие части кода тоже получают ссылки через свойство, принадлежащее Form.Я не могу делегировать функциональность интерфейса дочернему объекту, потому что слишком большая часть этой функциональности обслуживается элементами управления / компонентами в форме.Я не могу использовать TAggregatedObject или TContainedObject, чтобы связать время жизни сопряженных объектов, передаваемых вокруг формы, потому что класс TForm не наследуется от TinterfacedObject, а Delphi не поддерживает множественное наследование, поэтому я не могу смешивать TInterfacedObject с цепочкой наследования,Эта ситуация может привести к нарушениям доступа, если форма будет уничтожена, в то время как другой код содержит одну из ссылок интерфейса, переданных формой.Кто-нибудь может придумать хорошее решение этой проблемы?

Ответы [ 2 ]

9 голосов
/ 11 октября 2011

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

Вы можете использовать TAggregateObject или TContainedObject для своих нужд. Они не требуют, чтобы Форма была получена из TInterfacedObject. Все, что им требуется, это указатель интерфейса IInterface, а TComponent происходит от IInterface (и переопределяет _AddRef() и _Release() для отключения подсчета ссылок), так что вы можете передать саму форму (будучи * 1011) * потомок) в качестве обязательного IInterface указателя.

Это оставляет единственную оставшуюся проблему - закрытие формы, в то время как ссылки на активный интерфейс удерживаются другим кодом. Самое простое решение: 1) переписать этот код, чтобы он не удерживался на этих ссылках во время закрытия формы, или 2) не разрешать закрывать форму до тех пор, пока эти ссылки не будут освобождены.

2 голосов
/ 11 октября 2011

Примечание. Это будет работать только в том случае, если ваш потребитель также является производным от 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;
...