有没有办法将一个ObjectiveC块包装到函数指针?

我必须在iOS应用程序中为特定的C库提供C风格的callback。 callback没有void *userData或类似的东西。 所以我无法在上下文中循环。 我想避免引入全球范围来解决这个问题。 一个理想的解决scheme将是一个Objective-C块。

我的问题:有没有办法将一个块“投”到一个函数指针或包装/掩饰它?

从技术上讲,你可以访问块的函数指针。 但是这样做完全不安全,所以我当然不推荐它。 要看看如何,请考虑下面的例子:

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

运行产量:

 Howdy 1 ret = 1 

我们期望从block()直接执行这些块。 所以,你可以使用invoke作为你的函数指针。

但正如我所说,这是完全不安全的。 不要真的使用这个!

如果你想看到一个方法来写你想问的问题,那么看看这个: http : //www.mikeash.com/pyblog/friday-qa-2010-02-12-trampolining-blocks -with-可变-code.html

这只是一个伟大的写作,你需要做什么才能使这个工作。 不幸的是,它永远不会在iOS上工作(因为您需要将页面标记为可执行文件,而您不能在应用程序的沙箱中执行此操作)。 不过,这是一篇很棒的文章。

如果你的块需要上下文信息,而callback没有提供任何上下文,恐怕答案是一个明确的不。 块必须在某处存储上下文信息,所以你将永远无法将这样的块转换为无参数函数指针。

仔细devise的全局variables方法可能是这种情况下的最佳解决scheme。

MABlockClosure可以做到这一点。 但是,无论你需要什么,它都可能是矫枉过正的。

我知道这个问题已经解决了,但是对于感兴趣的各方,我还有另一个解决办法

将整个函数重新映射到一个新的地址空间。 新的结果地址可以用作所需数据的关键字。

 #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上testing)