guide basics objective-c iphone weak-references circular-reference

objective c - basics - Tengo una referencia circular. ¿Cómo puedo crear una referencia débil en Objective-C?



swift reference (4)

Estoy trabajando en una aplicación para iPhone. Tengo un objeto de la clase Row que necesita liberar numerosos objetos de la clase Block . Cada Block tiene actualmente una propiedad que retiene una variable de instancia de la clase Row .

@interface Block : UIImageView { Row *yCoord; } @property (nonatomic,retain) Row *yCoord; @end

Cada Row contiene un NSMutableArray de estos bloques.

@interface Row : NSObject { NSMutableArray *blocks; } -(void)addBlock:(Block*)aBlock; @end @implementation Row -(void)addBlock:(Block*)aBlock { [blocks addObject:aBlock]; aBlock.yCoord = self; } @end

Entiendo que esta es una referencia circular. La documentación de Apple indica que para desasignar un objeto con una referencia circular necesito una referencia débil en lugar de una referencia sólida (una propiedad de retención), pero no sigue y explica cómo lo hago exactamente. Planeo liberar y desasignar todos los Bloques dentro de una Fila, así como la propia Fila simultáneamente. ¿Cómo configuro una referencia débil dentro de cada Bloque en su Fila "padre"?


El uso de asignar para crear referencias débiles puede ser inseguro en un sistema multiproceso, en particular cuando cualquiera de los dos objetos puede ser retenido por un tercer objeto, y luego se usa para desreferenciar el otro objeto.

Afortunadamente, este es a menudo un problema de jerarquía, y el objeto que contiene la referencia débil solo se preocupa por el objeto al que hace referencia durante la vida útil del objeto referido. Esta es la situación habitual con una relación Superior <-> Subordinada.

Creo que el caso en el comentario del OP se asigna a esto, con Fila = Superior, Bloque = Subordinado.

En este caso, usaría un controlador para referirme al Superior del Subordinado:

// Superior.h @class Superior; @interface SuperiorHandle : NSObject { @private Superior* superior_; } // note the deliberate avoidance of "nonatomic" @property (readonly) Superior *superior; @end @interface Superior : NSObject { @private SuperiorHandle *handle_; // add one or more references to Subordinate instances } // note the deliberate avoidance of "nonatomic" @property (readonly) SuperiorHandle *handle; @end // Superior.m #import "Superior.h" @implementation SuperiorHandle @synthesize superior = superior_; - (id)initWithSuperior:(Superior *)superior { if ((self = [super init])) { superior_ = superior; // weak reference } } - (void)invalidate { @synchronized (self) { superior_ = nil; } } - (Superior *)superior { @synchronized (self) { // retain and autorelease is required to prevent dealloc before we''re ready, thanks to AndroidDev for pointing out this mistake return [[superior_ retain] autorelease]; } } @end @implementation Superior @synthesize handle = handle_; - (id)init { if ((self = [super init])) { handle_ = [[SuperiorHandle alloc] initWithSuperior:self]; } return self; } - (void)dealloc { [handle_ invalidate]; [handle_ release]; [super dealloc]; } @end // Subordinate.h @class Superior; @class SuperiorHandle; @interface Subordinate : NSObject { @private SuperiorHandle *superior_handle_; } @property (readonly) Superior *superior; @end // Subordinate.m #import "Subordinate.h" #import "Superior.h" @implementation Subordinate // no synthesize this time, superior''s implementation is special - (id)initWithSuperior:(Superior *)superior { if ((self = [super init])) { superior_handle_ = [superior.handle retain]; } return self; } - (void)dealloc { [superior_handle_ release]; [super dealloc]; } - (Superior *)superior { @synchronized (superior_handle_) { return superior_handle_.superior; } } @end

Algunas ventajas:

  1. Es hilo seguro. No hay forma de que la referencia débil contenida en el Subordinado se convierta en un puntero no válido. Puede volverse nulo pero eso está bien.
  2. Solo los objetos en sí necesitan saber acerca de la referencia débil incrustada. Todos los demás objetos pueden tratar al Subordinado como si tuviera una referencia regular a Superior.

Simplemente cámbielo para asignar en lugar de retener, no más referencias circulares.

@interface Block : UIImageView { Row *yCoord; } @property (nonatomic,assign) Row *yCoord; @end


Una referencia débil es simplemente una asignación (a menos que esté hablando de recolección de basura que es una lata de gusanos completamente separada, pero que no sufre ciclos de retención).

Normalmente, en Cocoa, Row mantendría los objetos Block (incluyéndolos en NSMutableArray), pero Block no retendría Row , cada uno simplemente los almacenaría en un ivar (con una propiedad "assign").

Mientras Row tenga cuidado de liberar cada Block antes de que se desasigne (es decir, su dealloc debería liberar el NSMutableArray, que liberará los Bloques siempre que nadie más tenga ningún indicador de ellos), entonces todo será desasignado según corresponda.

También puede tomar la precaución de poner a cero la referencia de fila de los bloques antes de eliminar la totalidad de la matriz, algo como:

- (void) dealloc { for (Block* b in _blocks) { b.row = nil; } [_blocks release]; [super dealloc]; }

donde _blocks es el ivar al que hace referencia la propiedad blocks.


Edición: dado que el autor de la pregunta aclaró que no está utilizando la recolección de basura (actualmente el iPhone no lo admite), mi consejo es evitar los ciclos haciendo que solo uno de los objetos retenga el otro, tal como lo haría con un delegado. Cuando use propiedades, use "asignar" en lugar de "retener" para lograr esto. Por ejemplo:

@property (nonatomic,assign) Row *yCoord;

El resto de mi respuesta respuesta se refiere a "referencias débiles" en términos de Objective-C 2.0 y GC.

Cuando estás trabajando con la recolección de basura (10.5+), se crea una referencia débil al prefijar una declaración de variable con __weak . Cuando asigna a esa variable, el GC (si está habilitado) realiza un seguimiento de la referencia y la eliminará automáticamente si todas las referencias fuertes al objeto referenciado desaparecen. (Si GC no está habilitado, el atributo __weak se ignora).

Por lo tanto, puede modificar de forma segura la respuesta anterior para jugar mejor con la recolección de basura (actualmente en 10.5 o más, y quizás algún día en el iPhone) de la siguiente manera: (Consulte la documentación de Apple relacionada ).

@property (nonatomic,assign) __weak Row *yCoord;

Para citar a Chris Hanson (donde puede encontrar información más detallada):

"Al prefijar una declaración de variable de instancia con __weak , le dice al recolector de basura que si es la única referencia a un objeto, el objeto debe considerarse coleccionable".

Aclararía eso diciendo "si no hay referencias no débiles a un objeto". Tan pronto como se elimine la última referencia segura, se podrá recopilar el objeto y todas las referencias débiles se pondrán a cero automáticamente.

Nota: Esto no está directamente relacionado con la creación de referencias débiles, pero también hay un atributo __strong , pero como las variables de objeto Objective-C son referencias fuertes por defecto, generalmente se usa solo para punteros C en bruto a elementos como estructuras o primitivas que El recolector de basura no tratará como raíces, y se recolectará debajo de usted si no los declara como fuertes. (Si bien la falta de __weak puede provocar ciclos de retención y pérdidas de memoria, la falta de __strong puede provocar el aplastamiento de la memoria y errores realmente extraños e insidiosos que se producen de forma no determinista y pueden ser bastante difíciles de localizar).