delphi - ¿Por qué las implementaciones de interfaz basadas en la memoria de fugas de TComponent?
interface memory-leaks (3)
Este código de Delphi mostrará una pérdida de memoria para una instancia de TMyImplementation:
program LeakTest;
uses
Classes;
type
MyInterface = interface
end;
TMyImplementation = class(TComponent, MyInterface)
end;
TMyContainer = class(TObject)
private
FInt: MyInterface;
public
property Impl: MyInterface read FInt write FInt;
end;
var
C: TMyContainer;
begin
ReportMemoryLeaksOnShutdown := True;
C := TMyContainer.Create;
C.Impl := TMyImplementation.Create(nil);
C.Free;
end.
Si TComponent es reemplazado por TInterfacedObject y el constructor se cambia a Crear (), la fuga desaparece. ¿Qué es diferente con TComponent aquí?
Muchas gracias por las respuestas. Para resumir: es fácil, pero equivocado, decir "Si está utilizando interfaces, se cuentan como referencia y, por lo tanto, se liberan para usted". - En realidad, cualquier clase que implemente una interfaz puede romper esta regla. (Y no habrá ninguna sugerencia o advertencia del compilador).
Diferencias en la implementación
-
TComponent._Release
no libera tu instancia. -
TInterfacedObject._Release
libera tu instancia.
Tal vez alguien pueda intervenir, pero mi opinión sobre esto es que TComponent
no está destinado a ser utilizado como un objeto de referencia de la forma en que usamos las interfaces.
Implementación de TComponent._Release
function TComponent._Release: Integer;
begin
if FVCLComObject = nil then
Result := -1 // -1 indicates no reference counting is taking place
else
Result := IVCLComObject(FVCLComObject)._Release;
end;
Se supone que un componente es propiedad y es destruido por otra cosa, típicamente una forma. En ese escenario, el recuento de referencias no se utiliza. Si pasa un componente como una referencia de interfaz, sería muy desafortunado si se destruyera cuando el método vuelve.
Por lo tanto, se ha eliminado el recuento de referencias en TComponent.
TComponent no implementa sus métodos _AddRef y _Release igual que TInterfacedObject. VCLComObject recuento de referencias a su propiedad VCLComObject , que debería ser algún otro objeto interconectado. Dado que TComponent no cuenta las referencias, no puede detectar cuándo su recuento de referencias llega a cero, por lo que no se libera.
La propiedad VCLComObject contiene una referencia de interfaz, que debería implementar IVCLComObject . Si se le ha dicho al objeto VCLComObject asociado de un componente que es el propietario del componente, entonces, cuando el recuento de referencia de la interfaz llegue a cero, destruirá su componente asociado. Se dice que posee el componente llamando a su método FreeOnRelease.
Todo esto está diseñado para que sea más fácil envolver los componentes VCL en objetos COM. Si ese no es su objetivo, entonces probablemente luchará contra otros aspectos inesperados del diseño en el camino, por lo que quizás desee volver a evaluar su motivación para hacer que sus componentes implementen interfaces en primer lugar.