Safe way in Delphi for a Form to distribute interface objects tied to its lifetime?

大城市里の小女人 提交于 2019-12-04 20:51:41

问题


I have a Delphi Form that provides the functionality behind an interface object that other parts of the code get references too via a property belonging to the Form. I can't delegate the interface functionality to a child object because too much of that functionality is serviced by controls/components on the form. I can't use TAggregatedObject or TContainedObject to link the lifetime of the interfaced objects being passed around to the Form because the TForm class does not inherit from TinterfacedObject and Delphi does not support multiple inheritance so I can't mix in TInterfacedObject into the inheritance chain. This situation can lead to access violations if a Form gets destroyed while some other code holds one of the interface references passed out by the Form. Can anyone think of a good solution to this problem?


回答1:


You can delegate the interface to a child object, just have that object contain an internal pointer to the Form so it can access the Form's controls when needed, no different then you are already doing right now.

You can use TAggregateObject or TContainedObject for your needs. They do not require the Form to derive from TInterfacedObject. All they do require is an IInterface interface pointer, and TComponent derives from IInterface (and overrides the _AddRef() and _Release() to disable reference counting), so you can pass the Form itself (being a TComponent descendant) as the required IInterface pointer.

That leaves the only issue remaining - the Form closing while active interface references are being held by other code. The simpliest solution is to either 1) rewrite that code to not hold on to those references while the Form is closing, or 2) don't allow the Form to close until those references have been released.




回答2:


Note: This will only work, if your consumer is also derived from TComponent.

To avoid the dead references you can query the IInterfaceComponentReference (available on every TComponent) from your form, call GetComponent on that interface and attach yourself to the FreeNotification of the returned Component/Form.

What happens now is: When the Form gets destroyed it will notify all "listners" that its going to destroy itself by calling the Notification method on the consumer with itself (form) as AComponent and opRemove as operation. Thus allowing you to nil your interface reference. But be aware that the object references and interface references must not be equal. Also make sure to call RemoveFreeNotification when you don't need the Notification any more to avoid unnecessary calls.

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;


来源:https://stackoverflow.com/questions/7719900/safe-way-in-delphi-for-a-form-to-distribute-interface-objects-tied-to-its-lifeti

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!