iphone memory-management ios4 retain objective-c-blocks

iphone - iOS 4 bloques y retener recuentos



memory-management ios4 (2)

Estoy empezando con bloques y Grand Central Dispatch. Me han dicho (y leído en la Documentación de Apple ) que se retiene cualquier objeto al que se hace referencia dentro de un bloque.

Por ejemplo:

^{ self.layer.transform = CATransform3DScale(CATransform3DMakeTranslation(0, 0, 0), 1, 1, 1); self.layer.opacity = 1; }

El "yo" queda retenido por lo que gotea. Para evitar eso, necesito asignarme uno mismo a:

__block Object *blockSelf = self;

y luego usar blockSelf lugar de self dentro de mi bloque.

Mi pregunta es: ¿qué sucede cuando su bloque tiene mucho más código y hace referencia a varios objetos? ¿Debo asignarlos a todos los objetos __block ? Por ejemplo:

^{ [self doSomething]; NSPredicate *predicate = [NSPredicate predicateWithFormat:@"prevName == %@", artistName]; [request setEntity:entity]; [request setPredicate:predicate]; Object *newObject = [[Object alloc] init]; [someArray addObject]; [newObject release]; }


No. El problema ocurre cuando su bloque retiene un objeto que lo retiene. Su bloque conservará cualquier objeto al que haga referencia, excepto aquellos anotados con __block . Por lo tanto:

// The following creates a retain cycle which will leak `self`: self.block = ^{ [self something]; };

self retiene el block , y el block retiene implícitamente el self . Esto también sucederá si hace referencia a las variables de instancia de self .

// The following avoids this retain cycle: __block typeof(self) bself = self; self.block = ^{ [bself something]; };

Las variables anotadas con __block son mutables (para los punteros, es decir, la dirección a la que apuntan se puede cambiar); como resultado, no tiene sentido retener ese objeto, ya que necesita tratar ese objeto como una variable local (como en, puede ser reasignado, afectando a un objeto fuera del alcance del bloque). Por lo tanto, __block no __block retenido por bloques.

Pero, ahora puede tener problemas imprevistos si intenta usar este bloque de ciertas maneras. Por ejemplo, si decide retrasar la invocación de este bloque de alguna manera, y la self ha sido desasignada para cuando ejecuta ese bloque, su programa se bloqueará, ya que está enviando un mensaje a un objeto desasignado. ¡Lo que necesita es una referencia débil, que no se proporciona de manera inmediata en el entorno no recolectado de basura!

Una solución es usar MAZeroingWeakRef para envolver su bloque; esto pondrá a cero el puntero para que solo termines enviando mensajes a nil si intentas enviar un mensaje a ti self después de que se haya desasignado:

MAZeroingWeakRef *ref = [MAZeroingWeakRef refWithTarget:self]; self.block = ^{ [ref.target something]; };

También he implementado una envoltura de referencia débil en Objective-C ++ , que proporciona el beneficio de una sintaxis más ligera:

js::weak_ref<SomeClass> ref = self; self.block = ^{ [ref something]; };

Debido a que js::weak_ref es una plantilla de clase, obtendrá una escritura fuerte (es decir, obtendrá advertencias en tiempo de compilación si intenta enviar a la referencia un mensaje al que no parece responder) . Pero el MAZeroingWeakReference de Mike es mucho más maduro que el mío, así que sugiero usar el suyo a menos que quieras ensuciarte las manos.

Para leer más sobre los problemas con __block y el caso de uso para referencias débiles, lea Cómo evitar los ciclos de retención con bloques, de manera correcta y la respuesta de Jonathan Rentzsch .


Yo diría que depende de lo que estés haciendo con tu bloque. Si no lo está almacenando en ningún lugar y lo usa en el lugar de definición (como ordenar una matriz usando un bloque), entonces se libera junto con las variables a las que se hace referencia dentro (ya que se crea en la pila y se marca para autorelease). Si lo está almacenando en algún lugar (una matriz, un diccionario o probablemente pase el bloque a alguna otra función), copy el bloque y autorelease con el autorelease antes de pasarlo.