2012-05-04 28 views
6

Có một công việc đơn giản cần thanh tiến trình. OpenSSL có hữu ích callback mà người ta có thể sử dụng cho rằng:Khối ObjC & callbacks C mở lại

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

với

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

Tuy nhiên tôi muốn gọi này từ ObjectiveC mà không cần quá nhiều ado:

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

Với Genrec : dưới dạng phương thức 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(); 
} 

Bây giờ hoàn thànhCallback(); hoạt động lộng lẫy và như mong đợi. Nhưng tôi nhận được một cảnh báo trình biên dịch/lỗi mà tôi không thể dập tắt để gọi lại tiến trình:

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

Vì vậy, tò mò - cách thích hợp để làm điều này là gì?

Xin cảm ơn,

Dw.

Trả lời

7

Tất cả mã chỉ được nhập vào câu trả lời này, hãy kiểm tra kỹ trước khi sử dụng!

Con trỏ và khối chức năng không giống nhau; trước đây chỉ là một tham chiếu đến mã, sau này là một bao đóng chứa cả mã và môi trường; họ không phải là trivially hoán đổi cho nhau.

Bạn có thể sử dụng con trỏ hàm trong Mục tiêu-C, do đó, đó là tùy chọn đầu tiên của bạn.

Nếu bạn muốn sử dụng các khối sau đó bạn cần phải tìm một cách để quấn một khối và vượt qua nó như là một tài liệu tham khảo chức năng ...

Định nghĩa của RSA_generate_key là:

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

Thứ tư đối số có thể là bất kỳ thứ gì và được chuyển làm đối số thứ ba cho hàm gọi lại; điều này cho thấy chúng ta có thể vượt qua khối cùng với một con trỏ tới một hàm C mà gọi đó là:

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

Và để gọi:

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

Cho dù bạn cần bất kỳ phôi cầu (ví ARC) là còn lại như một bài tập!

+0

Đáng yêu nhất! Và __bridge là tất cả những gì cần thiết cho ARC. Xấu hổ rằng người ta cần phải có callback() ở giữa - tức là người ta không thể vượt qua progressCallback như là một (void (*) (int, int, void *)) từ void của nó (^ __ strong) (int, int, void *) trực tiếp. –

+0

@ Dirk-WillemvanGulik - cách hợp lý duy nhất để hỗ trợ tự động truyền một khối, tức là đóng (mã con trỏ + môi trường), như một con trỏ hàm (chỉ là một con trỏ mã) sẽ tự động tạo ra các đoạn mã; và có nhiều hạn chế khác nhau. Mẫu chung trong C là chỉ định một con trỏ hàm lấy giá trị thừa, thường là kiểu 'void *', có thể được sử dụng để truyền một môi trường do người dùng định nghĩa và do đó tạo thủ công một bao đóng - và openssl theo mẫu đó và giải pháp ở trên chỉ sử dụng nó. – CRD

+0

Xóa. Cảm ơn (một lần nữa!).Đã đào vào NSStackBlock và làm cho ý nghĩa tổng thể. –

Các vấn đề liên quan