2012-10-22 6 views
9

iOSアプリケーションで特定のCライブラリ用のCスタイルのコールバックを提供する必要があります。コールバックにはvoid *userDataなどはありません。だから私は文脈でループすることができない。私はこれを解決するためにグローバルな文脈を導入することを避けたい。理想的な解決策はObjective-Cブロックです。ObjectiveCブロックを関数ポインタにラップする方法はありますか?

私の質問:ブロックを関数ポインタにキャストするか、何とかそれをラップ/クロークする方法はありますか?

答えて

6

技術的には、ブロックの関数ポインタにアクセスできます。しかし、それは全く安全ではないので、私は確かにそれをお勧めしません。 、次の例を考えてみ方法を確認するには:

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

は、実行中のその利回り:

私たちは純粋に block()と直接それらのブロックを実行するから期待するものである
Howdy 1 
ret = 1 

。したがって、関数ポインタとしてinvokeを使用することができます。

しかし、これは全く安全ではありません。これを実際に使用しないでください!あなたが求めているものを実行する方法の書き込みアップを見たい場合は

、これをチェックアウト: http://www.mikeash.com/pyblog/friday-qa-2010-02-12-trampolining-blocks-with-mutable-code.html

それはあなたがする必要があるだろうかだけの偉大な書き込みアップですこれを機能させるには残念ながら、あなたがアプリのサンドボックス内で実行できないような実行可能ファイルとしてページをマークする必要があるため、iOSで動作することは決してありません。それにもかかわらず、素晴らしい記事。

+0

私の無知を許してください、なぜそれは安全ではないのですか?私は構造体が内部のものであり、将来変更される可能性があるためです。 –

+0

そうですね:-)。 – mattjgalloway

+0

これは、コールバックにコンテキストを渡すことができないと言っているのに対し、ブロックには呼び出し関数にブロックを渡す必要があるという問題があります。 – newacct

4

ブロックがコンテキスト情報を必要とし、コールバックがコンテキストを提供していない場合、私は答えが明らかではないことを恐れています。ブロックはコンテキスト情報をどこかに格納しなければならないので、そのようなブロックを引数なしの関数ポインタにキャストすることはできません。

この場合、慎重に設計されたグローバル変数アプローチがおそらく最良の解決策です。

1

MABlockClosureはこれを正確に行うことができます。しかし、それはあなたが必要とするもののために残忍かもしれません。

0

私はこれが解決されたことを知っていますが、利害関係者には別の解決策があります。

関数全体を新しいアドレス空間に再マップします。新しい結果のアドレスは、必要なデータのキーとして使用できます。

#import <mach/mach_init.h> 
#import <mach/vm_map.h> 

void *remap_address(void* address, int page_count) 
{ 
    vm_address_t source_address = (vm_address_t) address; 
    vm_address_t source_page = source_address & ~PAGE_MASK; 

    vm_address_t destination_page = 0; 
    vm_prot_t cur_prot; 
    vm_prot_t max_prot; 
    kern_return_t status = vm_remap(mach_task_self(), 
           &destination_page, 
           PAGE_SIZE*(page_count ? page_count : 4), 
           0, 
           VM_FLAGS_ANYWHERE, 
           mach_task_self(), 
           source_page, 
           FALSE, 
           &cur_prot, 
           &max_prot, 
           VM_INHERIT_NONE); 

    if (status != KERN_SUCCESS) 
    { 
     return NULL; 
    } 

    vm_address_t destination_address = destination_page | (source_address & PAGE_MASK); 

    return (void*) destination_address; 
} 

もはや必要とされていないページを処理し、それがMABlockClosureより呼び出しあたりより多くのメモリを取ることに注意することを忘れないでください。

(iOSでテスト済み)

関連する問題