Respuesta corta
En lugar de acceder self
directamente, debe acceder indirectamente, desde una referencia que no se retendrá. Si no está utilizando el conteo automático de referencia (ARC) , puede hacer esto:
__block MyDataProcessor *dp = self;
self.progressBlock = ^(CGFloat percentComplete) {
[dp.delegate myAPI:dp isProcessingWithProgress:percentComplete];
}
La __block
palabra clave marca las variables que se pueden modificar dentro del bloque (no lo estamos haciendo), pero tampoco se retienen automáticamente cuando se retiene el bloque (a menos que esté utilizando ARC). Si hace esto, debe estar seguro de que nada más intentará ejecutar el bloque después de que se libere la instancia de MyDataProcessor. (Dada la estructura de su código, eso no debería ser un problema). Lea más sobre__block
.
Si está utilizando ARC , se conservará la semántica de los __block
cambios y la referencia, en cuyo caso deberá declararla __weak
.
Respuesta larga
Digamos que tienes un código como este:
self.progressBlock = ^(CGFloat percentComplete) {
[self.delegate processingWithProgress:percentComplete];
}
El problema aquí es que self retiene una referencia al bloque; mientras tanto, el bloque debe retener una referencia a sí mismo para obtener su propiedad de delegado y enviarle un método. Si todo lo demás en su aplicación libera su referencia a este objeto, su recuento de retención no será cero (porque el bloque lo señala) y el bloque no está haciendo nada malo (porque el objeto lo señala) y así el par de objetos se filtrará en el montón, ocupando memoria pero inalcanzable para siempre sin un depurador. Trágico, de verdad.
Ese caso podría solucionarse fácilmente haciendo esto en su lugar:
id progressDelegate = self.delegate;
self.progressBlock = ^(CGFloat percentComplete) {
[progressDelegate processingWithProgress:percentComplete];
}
En este código, self retiene el bloque, el bloque retiene al delegado y no hay ciclos (visibles desde aquí; el delegado puede retener nuestro objeto, pero eso está fuera de nuestras manos en este momento). Este código no correrá el riesgo de una fuga de la misma manera, porque el valor de la propiedad delegada se captura cuando se crea el bloque, en lugar de buscarlo cuando se ejecuta. Un efecto secundario es que, si cambia el delegado después de crear este bloque, el bloque seguirá enviando mensajes de actualización al antiguo delegado. Si eso puede suceder o no depende de su aplicación.
Incluso si fue genial con ese comportamiento, aún no puede usar ese truco en su caso:
self.dataProcessor.progress = ^(CGFloat percentComplete) {
[self.delegate myAPI:self isProcessingWithProgress:percentComplete];
};
Aquí está pasando self
directamente al delegado en la llamada al método, por lo que debe ingresarlo en algún lugar. Si tiene control sobre la definición del tipo de bloque, lo mejor sería pasar el delegado al bloque como parámetro:
self.dataProcessor.progress = ^(MyDataProcessor *dp, CGFloat percentComplete) {
[dp.delegate myAPI:dp isProcessingWithProgress:percentComplete];
};
Esta solución evita el ciclo de retención y siempre llama al delegado actual.
Si no puede cambiar el bloqueo, puede lidiar con él . La razón por la que un ciclo de retención es una advertencia, no un error, es que no necesariamente deletrean la fatalidad para su aplicación. SiMyDataProcessor
es capaz de liberar los bloques cuando se completa la operación, antes de que su padre intente liberarlo, el ciclo se interrumpirá y todo se limpiará correctamente. Si puede estar seguro de esto, lo correcto sería utilizar a #pragma
para suprimir las advertencias para ese bloque de código. (O utilice un indicador del compilador por archivo. Pero no desactive la advertencia para todo el proyecto).
También puede considerar usar un truco similar arriba, declarar una referencia débil o no retenida y usarla en el bloque. Por ejemplo:
__weak MyDataProcessor *dp = self; // OK for iOS 5 only
__unsafe_unretained MyDataProcessor *dp = self; // OK for iOS 4.x and up
__block MyDataProcessor *dp = self; // OK if you aren't using ARC
self.progressBlock = ^(CGFloat percentComplete) {
[dp.delegate myAPI:dp isProcessingWithProgress:percentComplete];
}
Los tres anteriores le darán una referencia sin retener el resultado, aunque todos se comportan de manera un poco diferente: __weak
intentará poner a cero la referencia cuando se libere el objeto; __unsafe_unretained
te dejará con un puntero no válido; __block
en realidad agregará otro nivel de indirección y le permitirá cambiar el valor de la referencia desde dentro del bloque (irrelevante en este caso, ya quedp
que no se usa en ningún otro lugar).
Lo mejor dependerá de qué código puede cambiar y qué no puede. Pero espero que esto te haya dado algunas ideas sobre cómo proceder.
dp
Esto es correcto, que si por alguna razón en el momento de la ejecución del bloque se liberará (por ejemplo, si se tratara de un controlador de vista y se apilara), ¿la línea[dp.delegate ...
causará EXC_BADACCESS?strong
oweak
?@weakify(..)
y@strongify(...)
que le permite usarself
en bloque de una manera no retenida.También existe la opción de suprimir la advertencia cuando esté seguro de que el ciclo se romperá en el futuro:
De esa manera, no tiene que andar con alias
__weak
,self
alias y prefijos de ivar explícitos.fuente
__weak id weakSelf = self;
tiene un comportamiento fundamentalmente diferente a la supresión de la advertencia. La pregunta comenzó con "... si está seguro de que el ciclo de retención se romperá"[array addObject:weakObject];
si se ha liberado el objeto débil, esto provoca un bloqueo. Claramente, eso no se prefiere sobre un ciclo de retención. Debe comprender si su bloqueo realmente dura lo suficiente como para justificar su debilitamiento, y también si desea que la acción en el bloque dependa de si el objeto débil sigue siendo válido.Para una solución común, tengo estos definir en el encabezado de precompilación. Evita la captura y aún habilita la ayuda del compilador al evitar usar
id
Luego en código puedes hacer:
fuente
self
dentro de su bloque @weakify (self); id block = ^ {@strongify (self); [self.delegate myAPIDidFinish: self]; };Creo que la solución sin ARC también funciona con ARC, usando la
__block
palabra clave:EDITAR: según las notas de la versión Transition to ARC , un objeto declarado con
__block
almacenamiento aún se conserva. Use__weak
(preferido) o__unsafe_unretained
(para compatibilidad con versiones anteriores).fuente
__block
palabra clave evitaba conservar su referencia. ¡Gracias! Actualicé mi respuesta monolítica. :-)Combinando algunas otras respuestas, esto es lo que uso ahora para que un yo débil mecanografiado lo use en bloques:
Lo configuré como un fragmento de código XCode con un prefijo de finalización de "welf" en métodos / funciones, que aparece después de escribir solo "nosotros".
fuente
warning => "es probable que la captura de uno mismo dentro del bloque conduzca a un ciclo de retención"
cuando se refiere self o su propiedad dentro de un bloque que está fuertemente retenido por self de lo que muestra la advertencia anterior.
así que para evitarlo tenemos que hacerlo una semana ref
así que en lugar de usar
deberíamos usar
nota: el ciclo de retención generalmente ocurre cuando de alguna manera dos objetos que se refieren entre sí por los cuales ambos tienen un recuento de referencia = 1 y su método delloc nunca se llama.
fuente
La nueva forma de hacerlo es usando @weakify y @strongify marco
Más información sobre @Weakify @Strongify Marco
fuente
Si está seguro de que su código no creará un ciclo de retención, o que el ciclo se interrumpirá más tarde, entonces la forma más sencilla de silenciar la advertencia es:
La razón por la que esto funciona es que si bien el análisis de Xcode tiene en cuenta el acceso de puntos a las propiedades y, por lo tanto,
se considera que tiene una retención por x de y (en el lado izquierdo de la asignación) y por y de x (en el lado derecho), las llamadas a métodos no están sujetas al mismo análisis, incluso cuando son llamadas a métodos de acceso a la propiedad que son equivalentes al punto de acceso, incluso cuando esos métodos de acceso a la propiedad son generados por el compilador, así que en
solo se ve que el lado derecho crea una retención (por y de x), y no se genera ninguna advertencia de ciclo de retención.
fuente