ObjC bloques y openssl C callbacks

Tener una tarea única y única que necesita una barra de progreso. OpenSSL tiene una callback útil que se puede usar para eso:

rsa=RSA_generate_key(bits,RSA_F4,progressCallback,NULL); 

con

 static void callback(int p, int n, void *arg) { .. stuff 

Sin embargo, quiero llamar a esto desde ObjectiveC sin demasiado ruido:

  MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:self.view animated:YES]; hud.mode = MBProgressHUDModeAnnularDeterminate; hud.labelText = @"Generating CSR"; [self genReq:^(int p,int n,void *arg) { hud.progress = --heuristic to guess where we are -- } completionCallback:^{ [MBProgressHUD hideHUDForView:self.view animated:YES]; }]; 

Con Genrec: como método objC:

 -(void)genReq:(void (^)(int,int,void *arg))progressCallback completionCallback:(void (^)())completionCallback { ..... rsa=RSA_generate_key(bits,RSA_F4,progressCallback,NULL); assert(EVP_PKEY_assign_RSA(pkey,rsa)); rsa=NULL; .... completionCallback(); } 

Ahora completionCallback (); funciona espléndidamente y como se esperaba. Pero obtengo una advertencia / error del comstackdor que no puedo sofocar para la callback del progreso:

  Passing 'void (^__strong)(int, int, void *)' to parameter of incompatible type 'void (*)(int, int, void *)' 

Entonces, tengo curiosidad, ¿cuál es la forma adecuada de hacer esto?

Gracias,

Dw.

Todo el código se escribe en esta respuesta, ¡testing cuidadosamente antes de usarlo!

Los pointers de function y los bloques no son lo mismo; el primero es solo una reference al código, este último es un cierre que contiene tanto código como un entorno; no son trivialmente intercambiables.

Por supuesto, puede usar pointers de function en Objective-C, por lo que esa es su primera opción.

Si desea utilizar bloques, entonces necesita encontrar una manera de envolver un bloque y pasarlo como una reference de function …

La definición de RSA_generate_key es:

 RSA *RSA_generate_key(int num, unsigned long e, void (*callback)(int,int,void *), void *cb_arg); 

El cuarto argumento puede ser cualquier cosa y se pasa como el tercer argumento a la callback; esto sugiere que podríamos pasar el bloque junto con un puntero a una function C que lo llama:

 typedef void (^BlockCallback)(int,int); static void callback(int p, int n, void *anon) { BlockCallback theBlock = (BlockCallback)anon; // cast the void * back to a block theBlock(p, n); // and call the block } - (void) genReq:(BlockCallback)progressCallback completionCallback:(void (^)())completionCallback { ..... // pass the C wrapper as the function pointer and the block as the callback argument rsa = RSA_generate_key(bits, RSA_F4, callback, (void *)progressCallback); assert(EVP_PKEY_assign_RSA(pkey,rsa)); rsa = NULL; .... completionCallback(); } 

Y para invocar:

 [self genReq:^(int p, int n) { hud.progress = --heuristic to guess where we are -- } completionCallback:^{ [MBProgressHUD hideHUDForView:self.view animated:YES]; } ]; 

¡Ya sea que necesite cualquier model de puente (para ARC) se deja como ejercicio!