2012-05-04 13 views
6

プログレスバーを必要とする簡単なワンタッチタスクがあります。私はあまり苦もなくObjectiveCからこれを呼び出したいしかしObjCブロックとopenssl Cコールバック

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

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

:OpenSSLには1つが、そのために使用できる便利な コールバックを持っている

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]; 
    }]; 

Genrecで: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(); 
} 

今すぐcompletionCallback();素晴らしいと期待どおりに動作します。 - これを行うための適切な方法は何ですか

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

だから好奇心:しかし、私は、進行状況コールバック鎮めることはできませんコンパイラの警告/エラーを取得しますか?ありがとうございます。

Dw。

答えて

7

すべてのコードはちょうどこの答えに入力されています。

関数ポインタとブロックは同じものではありません。前者はコードへの参照に過ぎず、後者はコードと環境の両方を含むクロージャです。彼らは自明に交換可能ではありません。

もちろん、Objective-Cで関数ポインタを使用することができます。これが最初のオプションです。

あなたがブロックを使用したい場合、あなたはブロックをラップし、関数参照としてそれを渡す方法を見つける必要がある...

RSA_generate_keyの定義は次のとおりです。

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

第四引数は何でもかまいませんし、3番目の引数としてコールバックに渡されます。

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

をあなたが(ARC用)の任意のブリッジキャストを必要とするかどうかである:

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(); 
} 

そして呼び出すために:これは、我々はそれを呼び出すC関数へのポインタと一緒にブロックを渡すことができます示唆します運動として残した!

+0

大好き! ARCには__bridgeだけが必要です。 (void)(int、int、void *)からprogressCallbackをvoid(* __ strong)に渡すことはできません。 ) 直接。 –

+0

@ Dirk-WillemvanGulik - 関数ポインタ(単なるコードポインタ)としてのブロック、すなわちクロージャ(コードポインタ+環境)の自動渡しをサポートする唯一の合理的な方法は、コードスタブを動的に生成することです。それにはさまざまな欠点があります。 Cの一般的なパターンは、ユーザ定義の環境を渡して手動でクロージャを構築するのに使うことができる余分な値(通常は 'void *'型)を持つ関数ポインタを指定することです。上ではそれを使用しています。 – CRD

+0

クリア。再度、感謝します!)。NSStackBlockを掘り下げて、完全に理解してください。 –

関連する問題