El principal problema con fuerte recuento de referencias es el potencial circular problema referencia . Esto ocurre cuando un interface
tiene una referencia fuerte a otro, pero el objetivo interface
tiene un puntero fuerte al original. Incluso cuando se eliminan todas las otras referencias, todavía se conservarán entre sí y no se liberarán. Esto también puede ocurrir indirectamente, mediante una cadena de objetos que podría tener el último en la cadena que hace referencia a un objeto anterior.
Ver la siguiente definición de la interfaz, por ejemplo:
IParent = interface
procedure SetChild(const Value: IChild);
function GetChild: IChild;
function HasChild: boolean;
property Child: IChild read GetChild write SetChild;
end;
IChild = interface
procedure SetParent(const Value: IParent);
function GetParent: IParent;
property Parent: IParent read GetParent write SetParent;
end;
La siguiente aplicación será definitivamente perder memoria:
procedure TParent.SetChild(const Value: IChild);
begin
FChild := Value;
end;
procedure TChild.SetParent(const Value: IParent);
begin
FParent := Value;
end;
En Delphi, tipo más común de variables de referencia en papel (es decir, la variante, dinámico array o cadena) resuelva este problema implementando copy-on-write. Desafortunadamente, este patrón no es aplicable a la interfaz, que no son objetos de valor, sino objetos de referencia, vinculados a una clase de implementación, que no se pueden copiar.
Tenga en cuenta que basó el lenguaje (como Java o C#) no sufren de este problema, ya que las referencias circulares son manejadas por su modelo de memoria: los objetos de por vida son mantenidos globalmente por el administrador de memoria. Por supuesto, aumentará el uso de memoria, ralentizará el proceso debido a acciones adicionales durante la asignación y las asignaciones (todos los objetos y sus referencias deben mantenerse en listas internas) y puede ralentizar la aplicación cuando el recolector de elementos no utilizados entra en acción.
Una solución común con los idiomas sin recogida de basura (como Delphi) es utilizar Punteros débiles, mediante los cuales se asigna la interfaz a una propiedad sin incrementar el recuento de referencias. Con el fin de crear fácilmente un puntero débil, la siguiente función se podría utilizar:
procedure SetWeak(aInterfaceField: PIInterface; const aValue: IInterface);
begin
PPointer(aInterfaceField)^ := Pointer(aValue);
end;
Por lo tanto, podría ser utilizado como tal:
procedure TParent.SetChild(const Value: IChild);
begin
SetWeak(@FChild,Value);
end;
procedure TChild.SetParent(const Value: IParent);
begin
SetWeak(@FParent,Value);
end;
se puede tratar de leer my blog post about weak references in Delphi - y su fuente asociada código: hemos implementado la referencia de debilidad directa y el manejo de la interfaz de referencia débil "cero" desde Delphi 6 hasta XE2.
De hecho, en algunos casos, deberá configurar los campos débiles de la interfaz en nil
, si libera la instancia de referencia antes de su hijo, para evitar cualquier problema de Infracción de acceso. Esto se llama "Poniendo a cero los punteros débiles", y lo que Apple implemented with the ARC model, y que intentamos implementar en Delphi.
1 para la explicación y la foto de perfil –
@JanDoggen: :-) animales siempre ha sido mi favorito del muppet –