views:

633

answers:

2

The IMP type in Objective-C represents a function pointer, as far I as understand. Is there any way to make an IMP from a block pointer? Thanks for your ideas.

UPDATE: Thanks, @bbum, for your great solution. I haven't modified my code to use it yet, but everyone can see the fruits of my labors here: A new metaclass for Objective-C.

+7  A: 

A block is effectively a structure that contains a bit of metadata, a reference to the code contained within the block and a copy of the const-copied data captured within the block.

Thus, no, there is no way to directly map between an IMP and a Block reference.

When a call to a block is compiled, the compiler emits a trampoline that sets up the stack frame and then jumps to the executable code within the block.

What you can do, though, is create an IMP that acts like that trampoline. For the following, you will need one specific IMP for each set of arguments & return types to be called. If you need to make this generic, you'll need to use assembly.

For this, I'm setting up unique block instances per instance of the class. If you want to have a generic block that acts across all instances, build a hash of SEL -> block per class.

(1) Associate blocks to act as IMPs using the associated references mechanism. Something like:

void (^implementingBlock)(id s, SEL _c, int v) = ^(id s, SEL _c, int v) {
     // do something here
}

objc_setAssociatedObject(obj, SELToImp, [implementingBlock copy]));

(2) implement a trampoline IMP something like this:

void void_id_sel_intTramp(id self, SEL _cmd, int value) {
    int (^block)(id s, SEL _c, int v) = objc_getAssociatedObject(self, _cmd);
    block(self, _cmd, value);
}

(3) stuff the above trampoline in for any selector via the Objective-C runtime's API (see method_setImplementation and like)

Caveats:

  • this was typed into StackOverflow. The real code will be similar, but likely slightly different.

  • the [implementingBlock copy] is critical; Blocks start life on the stack (usually)

bbum
Really cool solution, thanks a lot! I'll try it out. I ended up using a hash of blocks mapped against selectors, but I stored it in an `NSMutableDictionary` instance variable. I never thought to use `objc_setAssociatedObject`, though. That's brilliant.
Jonathan Sterling
By the way, do you think you could elaborate on the whole assembly thing? I'm fascinated by that, but I really don't understand very well how it all works.
Jonathan Sterling
+2  A: 

I believe Mike Ash, with some help from Landon Fuller, has solved this for the general case, including iOS, which is harder. He has a class on github which will turn a block into a function pointer.

You may also want to confer the announcement and some interesting follow-up discussion on cocoa-unbound.

Clay Bridges
Yeah, saw that the other day. Very cool. Thanks for the link!
Jonathan Sterling