Comprobando el tipo de bloque Objective-C?

Esto es principalmente una curiosidad, no estoy muy seguro de cuál es el uso práctico de esto, pero aquí va.

Dado que los bloques también son objetos Objective-C, ¿es posible verificar su tipo? Es decir, ¿responde al mensaje isKindOfClass: y cómo usar ese mensaje con respecto a los bloques?

Mi ingenuo pensó que probablemente sea así:

 -(void) aMethod { typedef int (^BlockA)(int x, int y); id blockVar = ...; // get a block from somewhere if([blockVar isKindOfClass:BlockA]) { BlockA blockVarA = blockVar; int result = blockVarA(1,2); } } 

El código anterior probablemente no funcionará. Pero si es posible verificar el tipo de bloque, ¿cuál es la forma correcta de hacerlo?

Puede hacer , un poco sorta.

Pero primero, vamos a desambiguar. -[NSObject isKindOfClass:] puede decirte que es un locking, y eso es todo. Por ejemplo, creo que esta línea de código, aparentemente y por desgracia UNA IDEA MALA , devolverá SI por bloques en el presente León y iOS 5.x:

 [myBlock isKindOfClass:NSClassFromString(@"NSBlock")] 

Eso no te ayudará a distinguir la firma de la función del bloque.

Pero se puede hacer, al enganchar la firma de la estructura interna documentada del bloque. Sigue el código para una aplicación de línea de comandos OS X de ejemplo, gran parte de la cual se extrajo de MABlockClosure de Mike Ash (gran explicación detallada ). (ACTUALIZACIÓN: el proyecto Github CTObjectiveCRuntimeAdditions también aparentemente proporciona código de biblioteca para este propósito).

 #import  struct BlockDescriptor { unsigned long reserved; unsigned long size; void *rest[1]; }; struct Block { void *isa; int flags; int reserved; void *invoke; struct BlockDescriptor *descriptor; }; static const char *BlockSig(id blockObj) { struct Block *block = (void *)blockObj; struct BlockDescriptor *descriptor = block->descriptor; int copyDisposeFlag = 1 << 25; int signatureFlag = 1 << 30; assert(block->flags & signatureFlag); int index = 0; if(block->flags & copyDisposeFlag) index += 2; return descriptor->rest[index]; } int main(int argc, const char * argv[]) { @autoreleasepool { int (^block)(NSNumber *) = ^(NSNumber *num) { NSLog(@"%@ %@", NSStringFromClass([num class]), num); return [num intValue]; }; NSLog(@"signature %s", BlockSig(block)); NSLog(@"retval %d", (int)block([NSNumber numberWithInt:42])); } return 0; } 

Ejecuta esto y deberías obtener algo como:

 [58003:403] signature i16@?0@8 [58003:403] __NSCFNumber 42 [58003:403] retval 42 

Los números en la firma (me dicen que son compensaciones) se pueden quitar para i@?@ simple.

La firma está en el formato @encode , que no es perfecto (por ejemplo, la mayoría de los objetos se asignan al mismo @ ), pero debe permitirle cierta capacidad para distinguir bloques con diferentes firmas en el tiempo de ejecución.

Si bien no está documentado en el enlace de Apple, mi prueba apunta a @? siendo el código para un tipo de bloque, que tiene sentido de la firma anterior. Encontré una discusión clang-developers sobre este tema que parece respaldar esto.

El ” BlockA ” en (^BlockA) es el nombre de la variable (en este caso, un typedef), no su clase.
Los bloques son objetos, pero no subclases regulares de NSObject . Solo implementan un subconjunto de los métodos. -isKindOfClass: probablemente solo se cuelgue.
Los bloques son del tipo NSMallocBlock o NSConcreteGlobalBlock , … dependiendo de dónde se crearon (montón, stack, …).

Parece que los bloques son de clases como __NSGlobalBlock__ , __NSStackBlock__ , o __NSMallocBlock__ , etc., cuya cadena de herencia finalmente va a NSBlock y luego a NSObject . De modo que podría probar si algo es un bloque haciendo [... isKindOfClass:NSClassFromString(@"NSBlock")] . Sin embargo, no parece haber ninguna forma de consultar la firma de un bloque (tipo de devolución y tipos de argumentos) en tiempo de ejecución, por lo que no podría distinguir entre bloques de diferentes firmas.

Además de que Apple no tiene nada que decir al respecto, al class_copyMethodList un bloque con class_copyMethodList y method_getName no se method_getName métodos obvios expuestos. Así que voy a decir que no es posible verificar su tipo.

Una vieja pregunta, pero de todos modos:

Si desea una forma simple de hacer esto: (comstackrlo con -fno-objc-arc)

 Class __NSGlobalBlock__CLASS () { static Class result = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ dispatch_block_t thisIsAGlobalBlock = ^{// a block with no variables will be a __NSGlobalBlock__ }; result = [[thisIsAGlobalBlock class] retain]; }); return result; }; Class __NSStackBlock__CLASS () { static Class result = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ __block dispatch_block_t thisIsAStackBlock = ^{ return ;// we really DON'T want infinate recursion thisIsAStackBlock();// including a reference to __block var makes this a __NSStackBlock__ }; result = [[thisIsAStackBlock class] retain]; }); return result; }; Class __NSMallocBlock__CLASS () { static Class result = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ __block dispatch_block_t thisIsAMallocBlock = Block_copy(// << turns the __NSStackBlock__ Block into a __NSMallocBlock__ ^{ return ;// we really DON'T want infinate recursion thisIsAMallocBlock();// including a reference to __block var makes this a __NSStackBlock__ }); result = [[thisIsAMallocBlock class] retain]; Block_release(thisIsAMallocBlock); }); return result; }; 

Código de prueba:

 @autoreleasepool { __block dispatch_block_t iAmAGlobalBlock = ^{ }; __block dispatch_block_t iAmAStackBlock = ^{ return; iAmAStackBlock(); }; dispatch_block_t iAmHeapBlock = Block_copy(iAmAStackBlock); dispatch_block_t iAmNotAHeapBlock = Block_copy(iAmAGlobalBlock); if ([iAmAGlobalBlock isKindOfClass:__NSGlobalBlock__CLASS()]) { NSLog(@"very great success!"); } if ([iAmAStackBlock isKindOfClass:__NSStackBlock__CLASS()]) { NSLog(@"another great success!"); } if ([iAmHeapBlock isKindOfClass:__NSMallocBlock__CLASS()]) { NSLog(@"also great success!"); } if ([iAmNotAHeapBlock isKindOfClass:__NSGlobalBlock__CLASS()]) { NSLog(@"yet another great success!"); } NSLog (@"Block classes, as reported by NSStringFromClass():\n__NSGlobalBlock__CLASS() = %@\n__NSStackBlock__CLASS() = %@\n__NSMallocBlock__CLASS() = %@\n[iAmAGlobalBlock class] = %@\n[iAmAStackBlock class] = %@\n[iAmHeapBlock class] = %@\n[iAmNotAHeapBlock class] = %@\n", NSStringFromClass(__NSGlobalBlock__CLASS()), NSStringFromClass(__NSStackBlock__CLASS()), NSStringFromClass(__NSMallocBlock__CLASS()), NSStringFromClass([iAmAGlobalBlock class]), NSStringFromClass([iAmAStackBlock class]), NSStringFromClass([iAmHeapBlock class]), NSStringFromClass([iAmNotAHeapBlock class]) ); Block_release(iAmHeapBlock); Block_release(iAmNotAHeapBlock);// not really needed, but since we did "Block_copy" it... } 
Intereting Posts