¿Hay alguna manera de envolver un bloque ObjectiveC en el puntero de function?

Debo proporcionar una callback de estilo C para una biblioteca de C específica en una aplicación de iOS. La callback no tiene void *userData o algo similar. Entonces, no puedo hacer un bucle en un context. Me gustaría evitar introducir un context global para resolver esto. Una solución ideal sería un bloque Objective-C.

Mi pregunta: ¿Hay alguna forma de "lanzar" un bloque en un puntero de function o de envolverlo / encubrirlo de alguna manera?

Técnicamente, podría tener acceso a un puntero de function para el bloque. Pero no es seguro hacerlo, así que no lo recomiendo. Para ver cómo, considere el siguiente ejemplo:

 #import <Foundation/Foundation.h> struct Block_layout { void *isa; int flags; int reserved; void (*invoke)(void *, ...); struct Block_descriptor *descriptor; }; int main(int argc, char *argv[]) { @autoreleasepool { // Block that doesn't take or return anything void(^block)() = ^{ NSLog(@"Howdy %i", argc); }; // Cast to a struct with the same memory layout struct Block_layout *blockStr = (struct Block_layout *)(__bridge void *)block; // Now do same as `block()': blockStr->invoke(blockStr); // Block that takes an int and returns an int int(^returnBlock)(int) = ^int(int a){ return a; }; // Cast to a struct with the same memory layout struct Block_layout *blockStr2 = (struct Block_layout *)(__bridge void *)returnBlock; // Now do same as `returnBlock(argc)': int ret = ((int(*)(void*, int a, ...))(blockStr2->invoke))(blockStr2, argc); NSLog(@"ret = %i", ret); } } 

Ejecutando eso se produce:

 Howdy 1 ret = 1 

Que es lo que esperaríamos de ejecutar puramente esos bloques directamente con block() . Por lo tanto, puede usar invoke como su puntero de function.

Pero como digo, esto es totalmente inseguro. ¡En realidad no usas esto!

Si quieres ver un resumen de una manera de hacer lo que estás preguntando, mira esto: http://www.mikeash.com/pyblog/friday-qa-2010-02-12-trampolining-blocks -con-mutable-code.html

Es solo una gran reseña de lo que deberías hacer para que esto funcione. Lamentablemente, nunca funcionará en iOS (ya que debe marcar una página como ejecutable que no está permitido en el entorno limitado de su aplicación). Pero, sin embargo, un gran artículo.

Si su bloque necesita información de context, y la callback no ofrece ningún context, me temo que la respuesta es clara. Los bloques tienen que almacenar información de context en alguna parte, por lo que nunca podrá lanzar dicho bloque en un puntero de function sin arguments.

Un enfoque de variable global cuidadosamente diseñado es probablemente la mejor solución en este caso.

MABlockClosure puede hacer exactamente esto. Pero puede ser exagerado para lo que necesites.

La descripción de imp_implementationWithBlock (que se encuentra en objc/runtime.h ) tiene esto que decir:

Crea un puntero a una function que llamará al bloque cuando se invoca el método.

Se supone que se debe utilizar para los methods, pero parece que puede usarlo para convertir cualquier bloque (vea los comentarios a continuación por newacct) en un puntero de function:

 #import <Foundation/Foundation.h> #import <objc/runtime.h> int main(int argc, const char * argv[]) { @autoreleasepool { NSString *message = @"Hello, World!"; NSString* (^upperCaseStringBlock)(NSString*) = ^NSString*(NSString* string) { return [string uppercaseString]; }; NSString* (*upperCaseString)(NSString*) = (void*)imp_implementationWithBlock(upperCaseStringBlock); NSString *upperCaseMessage = upperCaseString(message); NSLog(@"%@", upperCaseMessage); // Probably optional imp_removeBlock((IMP)upperCaseString); } return 0; }