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.