15

Estoy escribiendo una API que implica el manejo de eventos, y me gustaría poder usar bloques para los manejadores. Las devoluciones de llamada a menudo querrán acceder o modificarse. En el modo ARC, Clang advierte que los bloques que hacen referencia a uno mismo probablemente creen un ciclo de retención, lo que parece una advertencia útil que quiero mantener en general.Desactivar de manera compacta la advertencia de arc-retain-cycles para bloques que se auto referencian

Sin embargo, para esta parte de mi API, el ciclo de vida de la devolución de llamada y el objeto que contiene se mantienen de forma externa. Sé que puedo romper el ciclo cuando el objeto debe ser desasignado.

Puedo desactivar la advertencia de retener ciclo por archivo con #pragma clang diagnostic ignored "-Warc-retain-cycles", pero eso desactiva la advertencia para todo el archivo. Puedo rodear los bloques con #pragma clang diagnostic push y pop alrededor de esa advertencia, pero eso hace que los bloques sean feos.

También puedo obtener la advertencia de que se va al hacer referencia a una variable __weak que apunta a self en lugar de hacer referencia directamente a self, pero eso hace que los bloques sean menos agradables de usar.

La mejor solución que he llegado con esta macro es que hace el diagnóstico de discapacidad en todo el bloque:

#define OBSERVE(OBJ, OBSERVEE, PATH, CODE) \ 
[(OBJ) observeObject:(OBSERVEE) forKeyPath:(PATH) withBlock:^(id obj, NSDictionary *change) { \ 
_Pragma("clang diagnostic push") \ 
_Pragma("clang diagnostic ignored \"-Warc-retain-cycles\"") \ 
do { CODE; } while(0); \ 
_Pragma("clang diagnostic pop") \ 
}]; 

que funciona, pero no es muy visible para los usuarios de la API, que no permite anidada observadores, e interactúa mal con el editor de XCode. ¿Hay una mejor manera de desactivar o evitar la advertencia?

+10

Crear una referencia '__weak' a' self' toma literalmente una línea de código. Creo que solucionar el problema en este caso es mejor que tratar de aliviar los síntomas. ¿Cómo se hace referencia a 'weakSelf' en lugar de' self' haciendo que el bloque sea menos agradable de usar? –

+3

Es menos agradable de dos maneras. Los oyentes a menudo son bastante cortos, a veces una sola declaración. La declaración __weak dobla el tamaño del oyente. También significa que necesita calificar los accesos a la propiedad en lugar de usar un yo inferido. Estoy de acuerdo en que mi solución actual es probablemente peor que solo usar __weak, pero esperaba obtener una mejor a través de esta pregunta. –

+1

¿Puedes cambiar el prototipo de tu bloque de finalización para aceptar un argumento "propio"? Ahora el código donde pasa sus bloques se verá igual (excepto para aceptar un argumento adicional) y puede eliminar las advertencias. (es decir, hacer que su API pase el objeto en cuestión a su bloque) – nielsbot

Respuesta

7

Para empezar, hay una forma sencilla de desactivar las advertencias para ciertas líneas de código usando #pragma:

#pragma clang diagnostic push 
#pragma clang diagnostic ignored "<#A warning to ignore#>" 
<#Code that issues a warning#> 
#pragma clang diagnostic pop 

Pero no lo utilizaría en este caso particular, porque no va a solucionar el problema , simplemente lo ocultará al desarrollador. Preferiría ir con la solución que Mark propuso. Para crear una referencia débil, se puede hacer una de las siguientes exterior del bloque:

__weak typeof(self) weakSelf = self; // iOS ≥ 5 
__unsafe_unretained typeof(self) unsafeUnretainedSelf = self; // 5 > iOS ≥ 4 
__block typeof(self) blockSelf = self; // ARC disabled 
0

la nueva LLVM es mejor para detectar/prevenir tales conservan ciclos esperar el envío LLVM con iOS6 o hacer camino de Alex con la creación una var. débil

¡desactivar la advertencia es una mala idea!

1

Creo que desactivar la advertencia es actualmente la única forma correcta de hacerlo, ya que dice el compilador: No me importa este ciclo de retención, soy consciente de ello y me deshago del observador yo mismo. Introducir una referencia débil es una solución costosa, ya que viene con CPU y memoria en tiempo de ejecución.

0

me escribió la siguiente macro, que Creo, es muy inteligente ...

#define CLANG_IGNORE_HELPER0(x) #x 
#define CLANG_IGNORE_HELPER1(x) CLANG_IGNORE_HELPER0(clang diagnostic ignored x) 
#define CLANG_IGNORE_HELPER2(y) CLANG_IGNORE_HELPER1(#y) 

#define CLANG_POP _Pragma("clang diagnostic pop") 
#define CLANG_IGNORE(x)\ 
    _Pragma("clang diagnostic push");\ 
    _Pragma(CLANG_IGNORE_HELPER2(x)) 

Se le permite hacer todo tipo de cosas divertidas (sin Xcode que arengando), tales como ..

CLANG_IGNORE(-Warc-retain-cycles) 
[object performBlock:^(id obj){ [obj referToSelfWithoutWarning:self]; }]; 
CLANG_POP 

Usted puede poner en cualquier bandera de advertencia y Clang va prestar atención a sus caprichos ...

CLANG_IGNORE(-Warc-performSelector-leaks); 
return [self performSelector:someIllBegotSelector withObject:arcFauxPas]; 
CLANG_POP 

Por otra parte, las advertencias por lo general están ahí por una razón. Petardos.

0

Para resolver el problema del chasquido de crear una referencia débil, puse eso en una macro. Se utiliza el preprocesador para crear una nueva var con el mismo nombre pero con un prefijo ('w', en este caso, he evitado 'débil' porque eso sería una exageración y el desorden más con las reglas de capitalización):

#define WEAK_VAR(NAME) __unsafe_unretained typeof(NAME) w##NAME = NAME 

... 
WEAK_VAR(self); 
self.block = ^{ 
    [wself doStuff]; 
}; 

Si, otoh, una referencia débil no es deseable, ¡no la use! Me gusta la solución de Nielsbot de pasar el objeto como un parámetro (cuando sea posible, por supuesto).

Cuestiones relacionadas