2009-04-26 13 views
10

Hay dos objetos A y B. A crea B y lo conserva. B tiene una variable de instancia que apunta a A, reteniéndola. Entonces ambos se mantienen uno al otro. Algunas personas dicen que esta fuerte conexión no se puede romper nunca más.Retain Cycles: ¿Por qué es eso tan malo?

¿Pero realmente es así?

Si B liberaría A, entonces A podría liberar fácilmente B, por lo que B sería desasignado. A sería desasignado tan pronto como otro propietario (supongo que haya alguien) lo libere.

¿O este problema solo se aplica en un caso en el que A no crea B, sino que solo hace una referencia fuerte a través de retenerlo en una variable de instancia? Todavía no veo por qué esa conexión no podría romperse de nuevo.

Respuesta

15

Los ciclos no son malos, pero a menudo se evitan porque pueden dificultar el aseguramiento de que no haya pérdidas de memoria. Las fugas ocurren especialmente cuando los objetos son 'contados por referencia'. En un idioma o sistema que utiliza el recuento de referencias, un objeto realiza un seguimiento del número de referencias que lo señalan. Cada vez que se borra una referencia, el conteo disminuye, cuando el recuento llega a cero, no hay referencias y, por lo tanto, el objeto se puede eliminar.

Esto generalmente se soluciona solo y funciona bien sin pensarlo cuidadosamente. Si tiene un grupo de objetos sin ciclos y deja caer su referencia al objeto raíz, se eliminará, esto significa que se eliminarán las referencias que tiene a los objetos que posee, los objetos a los que se hará referencia tendrán sus recuentos de referencia ir a cero. Se eliminarán y la cascada hará que se eliminen todos los objetos.

Pero ... si tiene un ciclo, esta cascada no funciona. Puede tener un grupo de objetos y ya no los quiere, por lo que deja caer la única referencia que tiene a estos objetos, pero como hay un ciclo, los objetos se referencian entre sí. Esto significa que sus recuentos de referencia nunca pasan a cero, y no se eliminan. Esta es una pérdida de memoria.

Claramente, puede hacer una gestión cuidadosa y romper los ciclos antes de soltar su referencia a un grupo de objetos que ya no desea. Pero ... como acabo de decir, esto requiere una administración cuidadosa. Es muy fácil equivocarse. Esta es una de las razones principales por las que se producen fugas de memoria.

Para evitar el riesgo de fugas y la complicada tarea de romper los ciclos correctamente cuando ya no necesita un grupo de objetos, los programadores generalmente intentan evitar los ciclos. Esto se vuelve más importante en grandes proyectos con muchos programadores donde nadie entiende todo el sistema. Si hubiera ciclos, los programadores tendrían que tener cuidado y pasar mucho tiempo estudiando el código de los demás para evitar ciclos.

Algunos lenguajes con recolectores de basura (por ejemplo, C#) pueden eliminar un grupo de objetos que ya no se necesitan aunque el grupo contenga ciclos.

+2

El recolector de basura de Objective-C (cuando está habilitado) también puede eliminar grupos de retención de bucles (casi cualquier recolector de basura) pero esto no es relevante en el iPhone, donde la recolección de basura de Objective-C no es compatible. –

2

El problema es este: A señala y conserva B, y B apunta y conserva A. Cuando no hay otras referencias a A o B, no habrá forma de liberarlos, porque su aplicación no lo hace tener alguna referencia a ellos en ese punto. Esto se denomina ciclo de referencia, y es un tipo de pérdida de memoria común en cualquier sistema contado de referencia. La forma en que esto se resuelve en la mayoría de los lenguajes de alto nivel es mediante el uso de la recolección de basura en lugar del recuento de referencias.

+0

Gracias. Pero ¿por qué no debería haber otra referencia a A o B? ¿En qué caso podría tener dos objetos en los que mi aplicación no tiene referencia? – Thanks

+0

Si tiene otras referencias a A o B, no hay problema. Solo cuando pierdes esas referencias (como si salieran del alcance) es un problema. – Zifre

7

Un ciclo de retención puede romperse, si usted lo conoce. Por lo general, conduce a errores desagradables (pérdidas de memoria). En su ejemplo:

A* a = [[A alloc] initAndCreateB]; 

Ahora, una instancia de B sin nombre (creado por A) tiene una cuenta de retención de 1.Puesto que tenemos una referencia a la A y la instancia B anónima contiene una fuerte referencia a A, de A retener el recuento es 2.

Digamos, hemos terminado usando R:

[a release]; 
return 12; 

Ahora, una de retener recuento es 1. No se dará a conocer, se perderá la memoria. Es por eso que los ciclos de retención son malos.

+0

Gracias. Para mi comprensión, no parece que haya dos objetos diferentes. A debe ser una Superclase de B, para que la estructura de datos de B pueda caber en A. Pero luego de hacer eso, ¿no sería simplemente un anónimo B? Podría estar equivocado. Es tarde;) – Thanks

+0

A no tiene que ser una superclase de B. Simplemente tiene un ivar que apunta a una instancia B. El B es un objeto separado. Piense en las clases que ve todos los días: puede tener un NSArray y NSString y un NSDate como variables de instancia. Esas son * no * subclases de su clase personalizada. Son clases independientes que los tuyos usan. – Chuck

3

La forma de romper un bucle de retención es tener un método separado "cerrado".

es decir

A retains B 
B retains A 

cuando haya terminado, llamar a un método (lo llamaré "close") en donde A libera B. A continuación, puede liberar A y todo el bucle va a liberar (asumiendo que no hay retenciones en otro lugar).

Cuestiones relacionadas