I’m writing an Obj-C wrapper to a standard C API. I would like to replace C callbacks by blocks.
Let imagine a C API:
void my_async_function(void (* callback)(void *), void *udata);
The Obj-C wrapper looks like this:
- (void)myAsyncFunction:(dispatch_block_t)block
{
void *udata = (__bridge_retained void *)block;
my_async_function(my_callback, udata);
}
void my_callback(void *udata)
{
dispatch_block_t block = (__bridge_transfer dispatch_block_t)udata;
block();
}
__bridge_retained and __bridge_transfer work well in many cases but on blocks, they result in a very strange behavior.
The assembly code of myAsyncFunction: has no retain at all (Xcode 4.4, ARC, O3).
What is very strange is that the following core, generates a objc_retainBlock, what I expected for myAsyncFunction:
void *a_global_var;
- (void)myAsyncFunction2:(dispatch_block_t)block
{
void *udata = (__bridge_retained void *)block;
a_global_var = udata;
my_async_function(my_callback, udata);
}
Can we call that a bug of the compiler?
If not, what rule the compiler is following?
Similar topics:
Try:
Ordinarily, the compiler synthesizes in a
Block_copycall when you assign a block pointer to a location where it could outlive the block structure it references.However, the compiler has no way of knowing what happens to the void* after you pass it into the C api, and anyway you’re overriding whatever the compiler might think it ought to do with the
__bridge_retainedcall. Retaining a block when storing a reference isn’t enough.Also, even with this change, your callback must be called exactly once, as it’s responsible for releasing the block. If it never gets called, you’ll leak the block. If it gets called more than once, you’ll crash. So you’ll probably want to make instances of your wrapper class responsible for managing the memory of the block, unless the C api allows you to provide a cleanup function, which you could use to release the block.