2011-10-10 9 views
7

Tengo un formulario de Delphi que proporciona la funcionalidad detrás de un objeto de interfaz que otras partes del código obtienen referencias también a través de una propiedad que pertenece al formulario. No puedo delegar la funcionalidad de la interfaz a un objeto secundario porque los controles/componentes en el formulario tienen demasiada funcionalidad. No puedo usar TAggregatedObject o TContainedObject para vincular el tiempo de vida de los objetos interconectados que se pasan al formulario porque la clase TForm no hereda de TinterfacedObject y Delphi no admite herencia múltiple, por lo que no puedo mezclar en TInterfacedObject en la cadena de herencia. . Esta situación puede provocar violaciones de acceso si un formulario se destruye mientras que otro código contiene una de las referencias de interfaz que el formulario expidió. ¿Alguien puede pensar en una buena solución a este problema?¿Manera segura en Delphi para un formulario para distribuir objetos de interfaz vinculados a su tiempo de vida?

Respuesta

9

Puede delegar la interfaz a un objeto secundario, simplemente haga que ese objeto contenga un puntero interno al formulario para que pueda acceder a los controles del formulario cuando sea necesario, no diferente de lo que ya está haciendo en este momento.

Puede usar TAggregateObject o TContainedObject para sus necesidades. No requieren que el formulario derive de TInterfacedObject. Lo único que hacen es requerir un puntero de interfaz de IInterface, y TComponent deriva de IInterface (y anula la _AddRef() y _Release() desactivar la cuenta de referencias), por lo que puede pasar el propio formulario (por ser un descendiente TComponent) como el puntero IInterface requerido.

Eso deja el único problema restante: el cierre del Formulario mientras las referencias de la interfaz activa están siendo retenidas por otro código. La solución más simple es: 1) reescribir ese código para no mantener esas referencias mientras el Formulario se está cerrando, o 2) no permitir que el Formulario se cierre hasta que esas referencias hayan sido liberadas.

+0

Lebeau, gracias Voy a utilizar esa información para revisar mi código. –

+0

Perdón por la última pregunta, pero ¿puede indicarme una buena referencia que explique cuándo utilizar TContainedObject en lugar de TAggregatedObject? He mirado fijamente la Ayuda de Delphi por un tiempo y realmente no puedo aclarar las diferencias de casos de uso. –

2

Nota: Esto solo funcionará, si su consumidor también se deriva de TComponent.

Para evitar las referencias muertas puede consultar la IInterfaceComponentReference (disponible en todos los TComponent) de su formulario, llame GetComponent en esa interfaz y apegarse a la FreeNotification del Componente/Formulario devuelto.

Lo que sucede ahora es: cuando la forma se destruye se notificará a todos los "listners" que su va a destruirse a sí misma llamando al método Notification en el consumidor con ella misma (forma) como AComponent y opRemove como la operación. De este modo, te permite anular la referencia de tu interfaz. Pero tenga en cuenta que las referencias de objeto y las referencias de interfaz no deben ser iguales. También asegúrese de llamar al RemoveFreeNotification cuando ya no necesite la Notificación para evitar llamadas innecesarias.

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; 
+0

¡Agradable! No sabía sobre eso. ¿Podría ampliar un poco su declaración: "pero tenga en cuenta que las referencias de los objetos y las referencias de interfaz no deben ser iguales"? –

+1

Bueno, varias razones: a) puede hacer una implementación personalizada de 'QueryInterface' que puede devolver un nuevo objeto interconectado en cada llamada. b) En segundo lugar, puede delegar la interfaz a una propiedad (Win32), lo que podría volver a crear un nuevo objeto cada vez. c) La implementación de 'TObject.GetInterface' agrega un interfaceOffset a la dirección.(Pero estos elementos internos están desafortunadamente más allá del conocimiento de mayo) La interfaz recientemente introducida (Delphi 2010) para la fundición de objetos se realiza básicamente mediante la consulta de una interfaz de marcador que devuelve la dirección de entrada del objeto. –

+0

¿Qué pasará si el objeto y las referencias de la interfaz son iguales y cuál es el error más común cometido por un programador que termina creando esa situación no deseada? –

Cuestiones relacionadas