2011-05-09 15 views
22

Me tropecé con un problema curioso con BOOL tipo de retorno en bloques. Que tiene la siguiente definición:Tiene problemas con BOOL tipo de retorno en Objective-C bloques

typedef BOOL (^BoolBlock)(void); 

... este código pasa:

BoolBlock foo = ^{ return YES; }; 

... pero esto falla al compilar:

BoolBlock bar = ^{ return YES || NO; }; 

Con el siguiente mensaje de error:

Incompatible block pointer types inicializar 'BoolBlock' (también conocido como 'BOOL (^) (void)') con una expresión de tipo 'int (^) (void)'

puedo solucionar el problema utilizando una conversión explícita, pero ¿No debería funcionar esto sin eso? ¿Hay una mejor solución?

Respuesta

11

Probablemente esté pensando que el operador || funciona en idiomas como Ruby y Python, donde vuelve en el primer operando que es verdad. En C, devuelve 1 si el operando es verdadero y 0 en caso contrario; por eso cree que está devolviendo un número entero.

+3

Entonces, ¿por qué algo como '- (BOOL) foo {return SÍ || NO; } 'compilar? En otras palabras, ¿por qué importa la distinción entre entero y [BOOL] (http://stackoverflow.com/questions/541289) en el caso de bloque y no en el de la función? – zoul

+2

@zoul: la función tiene un tipo de devolución explícita, por lo que la int se convertirá implícitamente en BOOL. Pero el bloque que se crea no tiene un tipo de devolución explícito, por lo que infiere su tipo de devolución de la declaración de devolución (esta es una habilidad especial que tienen los bloques). Estás devolviendo un int, por lo que infiere que el bloque devuelve un int. Solo entonces, después de que el tipo del bloque se haya inferido por completo, el compilador verifica ese tipo con el tipo de la variable a la que lo está asignando, por lo que obtiene una discrepancia de tipo. – Chuck

15

|| el operador devuelve el tipo int como dijo Chuck.

BoolBlock bar = ^{ return (BOOL)(YES || NO); }; 

o

BoolBlock bar = ^BOOL (void){ return YES || NO; }; 
BoolBlock bar = ^BOOL(){ return YES || NO; }; // warns in gcc, ok with clang 
+1

Gracias, indicando que el tipo de devolución en la definición de bloque ayuda y es una solución mejor que el lanzamiento antes de la devolución. Clang está contento con '^ BOOL() {...}'. – zoul

+1

Gracias! Tuve el mismo problema cuando intentaba usar ... = BOOL^en lugar de ... =^BOOL – ribeto

5

Como otros han dicho, la razón por la que está recibiendo el error es que e0 || e1 devuelve un int independientemente de los tipos de e0 y e1. Dado que el compilador infiere el tipo de devolución de bloque basado en la declaración (s) return, tiene un bloque que devuelve int y está intentando asignarlo a una variable de bloque cuyo tipo de devolución de bloque es BOOL.

yo personalmente prefiero esta sintaxis:

BoolBlock bar = ^BOOL { return YES || NO }; 

para evitar el error, por lo que es claro que el tipo de retorno bloque es BOOL. El valor de r, un literal de bloque, se entiende como un bloque cuyo tipo de devolución es BOOL y el compilador aplica las conversiones de C habituales.

En cuanto a por qué sucede esto, es una decisión de diseño, aunque no parece estar documentado explícitamente. Los bloques son una nueva función de idioma. Los diseñadores de compiladores han decidido que deberían tener una semántica más estricta en bloques, es decir, la asignación de tipos de punteros de bloque debe tener tipos estrictamente coincidentes, y refuerzan esta semántica más estricta cuando asignan un bloque a una variable de bloque independientemente del valor r un puntero de bloque o un literal de bloque.

Dado que no existe un estándar ISO/IEC que cubra bloques en C o C++, los diseñadores de compiladores son libres de tomar estas decisiones. Apple ha enviado bloques al ISO/IEC JTC1/SC22/WG14 como WG14/N1370 y WG14/N1451 y, si lo aceptan, este comportamiento (o alguna variante del mismo) debe estandarizarse y documentarse.

El código fuente de Clang tiene un comentario que indica que la asignación de punteros de bloque es más estricta que la asignación de punteros a funciones.

Les he preguntado personalmente acerca de esto.

+0

^BOOL {} es válido? Parece que [Descripción general de los bloques] (http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1370.pdf) y [Propuesta de bloques] (http: //www.open-std. org/jtc1/sc22/wg14/www/docs/n1451.pdf) no mencionen dicha notación. only^void (void) {},^(void) {} ​​y^{}. –

+0

@Kazuki La propuesta dice 'Los cierres que tienen una lista de argumentos nullary pueden especificarlo como tal al declararlo como (nulo) u omitir el (vacío) por completo' –

+0

@Kazuki ¡Gracias por el enlace de la propuesta! –

Cuestiones relacionadas