2012-05-04 15 views
6

Tiene una tarea única simple que necesita una barra de progreso. OpenSSL tiene una utilidad de devolución de llamada, que se puede utilizar para ello:ObjC blocks & openssl C devoluciones de llamada

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 demasiados preámbulos:

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 consigo una advertencia del compilador/error que no puedo sofocar la devolución de llamada para el progreso:

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

Así que tengo curiosidad - ¿cuál es la manera apropiada de hacer esto?

Gracias,

Dw.

Respuesta

7

Todo el código se tipea en esta respuesta, pruébelo con cuidado antes de usarlo.

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

Por supuesto, puede utilizar punteros a función en Objective-C, por lo que es su primera opción.

Si desea utilizar bloques Luego hay que encontrar una manera de envolver un bloque y pasarlo como una referencia de funciones ...

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 el argumento puede ser cualquier cosa y se pasa como el tercer argumento para la devolución de llamada; esto sugiere que podría pasar el bloquejunto con un puntero a una función 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]; 
          } 
]; 

sea que necesite ningún moldes puente (por ARC) es izquierda como un ejercicio!

+0

Most Lovely! Y __bridge es todo lo que se necesita para ARC. Es una pena que uno necesite tener la devolución de llamada() en el medio - es decir, que uno no puede pasar el progressCallback como un (void (*) (int, int, void *)) de su vacío (^ __ strong) (int, int, void *) directamente. –

+0

@ Dirk-WillemvanGulik: la única forma razonable de admitir el pase automático de un bloque, es decir, el cierre (puntero de código + entorno), como un puntero de función (solo un puntero de código) sería generar dinámicamente fragmentos de código; y eso tiene varios inconvenientes. El patrón común en C es especificar un puntero de función tomando un valor extra, generalmente de tipo 'void *', que se puede usar para pasar un entorno definido por el usuario y así construir manualmente un cierre - y openssl sigue ese patrón y la solución arriba simplemente lo usa. – CRD

+0

Clear. ¡Gracias de nuevo!).Has cavado en NSStackBlock y tiene mucho sentido. –

Cuestiones relacionadas