iOS 4.3: imp_implementationWithBlock()

In iOS 4.3, there are 3 rather low level functions in the runtime that provide a new kind of bridge between Objective-C and Blocks with a specific goal of facilitating the dynamic generation of method implementations.

Specifically:

IMP imp_implementationWithBlock(void *block);
void *imp_getBlock(IMP anImp);
BOOL imp_removeBlock(IMP anImp);

In particular, imp_implementationWithBlock() takes a block as a parameter, copies it to the heap, and returns a trampoline that allows the block to be used as the implementation — the IMP — of a method in any Objective-C class (as long as the block’s arguments and the method’s arguments are compatible).

Quite literally, this allows:

int j = 12;
IMP skewIMP = imp_implementationWithBlock(^(id _s, int k) { return k*j; });

Where skewIMP would then contain a function pointer that could be used as the IMP for a method declared like:

- (int)skew:(int)k;

Details on the flip side….Note that imp_implementationWithBlock() does not return a generic function pointer that can be used to call the block like a function. The key detail being that an IMP always has at least two arguments; (id self, SEL _cmd).

To declare the Block, though, you drop the SEL _cmd argument while keeping the rest.

Like this:

-(void)doSomething:
void(*doSomethingIMP)(id s, SEL _c);
void(^doSomethingBLOCK)(id s);

-(void)doSomethingWith:(int)x;
void(*doSomethingWithIMP)(id s, SEL _c, int x);
void(^doSomethingWithBLOCK)(id s, int x);

-(int)doToThis:(NSString*)n withThat:(double)d;
int(*doToThis_withThatIMP)(id s, SEL _c, NSString *n, double d);
int(^doToThis_withThatBLOCK)(id s, NSString *n, double d);

The reason for this pattern has to do with the Objective-C and Blocks ABI. A method is really just a C function with two arguments at the beginning; the object being messaged and the selector of the method being executed. Similarly, a call to a Block is exactly like a C function with one argument at the beginning; a reference to the block (as described in the Block ABI on the llvm.org site).

As explained in the intimate tour of objc_msgSend() I wrote up a while ago, making objc_msgSend fast requires three optimizations and/or requirements:

  • Don’t Mess With Registers (unless absolutely necessary)
  • Tail Call Optimized
  • Don’t Mess with the Argument List

imp_implementationWithBlock() follows the same mantra; the returned function pointer is a tiny trampoline that does a minimally intrusive edit of the argument list and then tail calls into block’s implementation. Thus, it is fast and universally applicable to method implementations.

The key is that a method’s implementation always has two pointer sized arguments at the head of the argument list; (self & _cmd). The trampoline overwrites the second argument (_cmd) with the first argument (self), then shoves the reference to the block into the first argument slot, and finally tail calls to the block’s implementation.

More importantly, the function pointer — the IMP — returned by imp_implementationWithBlock() is an IMP like any other. It can be passed to any IMP taking API and the type string passed to class_addMethod() is unchanged from a regular “compile time IMP”.

End result?

The block is invoked with self being the first parameter to the block and, most importantly to the sanity of the engineers who wrote this (and for performance reasons), all other arguments, from none to many, are left entirely untouched in the process.

The other two functions — imp_getBlock() and imp_removeBlock() — are provided for completeness’s sake. Obviously, removing and destroying a block that is currently the IMP of a method is a great way to create a “quick quit” easter egg in your application.

Putting it Together

Using Xcode 4.0 and iOS 4.3, create a new iOS View Based Application.

Replace the code in the provided main.m with the following:

#import <UIKit/UIKit.h>
#import <objc/runtime.h>

@interface Answerer:NSObject
@end

@interface Answerer(DynamicallyProvidedMethod)
- (int)answerForThis:(int)a andThat:(int)b;
- (void)boogityBoo:(float)c;
@end

@implementation Answerer
@end

int main(int argc, char *argv[])
{
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    int (^impyBlock)(id, int, int) = ^(id _self, int a, int b)
    {
        return a+b;
    };
    // grab an instance of the class we'll modify next    
    Answerer *a = [Answerer new];
    // create an IMP from the block
    int (*impyFunct)(id, SEL, int, int) = (void*) imp_implementationWithBlock(impyBlock);
    // call the block, call the imp. Note the argumentation differences
    NSLog(@"impyBlock: %d + %d = %d", 20, 22, impyBlock(nil, 20, 22));
    NSLog(@"impyFunct: %d + %d = %d", 20, 22, impyFunct(nil, NULL, 20, 22));

    // dynamically add the method to the class, then invoke it on the previously
    // created instance (or we could create the instance after adding, doesn't matter)
    class_addMethod([Answerer class], @selector(answerForThis:andThat:), (IMP)impyFunct, "i@:ii");
    NSLog(@"Method: %d + %d = %d", 20, 22, [a answerForThis:20 andThat:22]);
    
    // It is just a block;  grab some state (the selector & a variable)
    SEL _sel = @selector(boogityBoo:);
    float k = 5.0;
    IMP boo = imp_implementationWithBlock(^(id _self, float c) {
        NSLog(@"Executing [%@ -%@%f] %f",
              [_self class], NSStringFromSelector(_sel), c,
              c * k);
    class_addMethod([Answerer class], _sel, boo, "v@:f");

    // call the method
    [a boogityBoo:3.1415];
    
    // clean up
    [a release];
    [pool release];
    return 0;
}

And the output:

ImpityImp[2298:207] impyBlock: 20 + 22 = 42
ImpityImp[2298:207] impyFunct: 20 + 22 = 42
ImpityImp[2298:207] Method: 20 + 22 = 42
ImpityImp[2298:207] Executing [Answerer -boogityBoo:3.141500] 15.707500


13 Responses to “iOS 4.3: imp_implementationWithBlock()”

  1. Michael Tsai - Blog - imp_implementationWithBlock() says:

    [...] Bill Bumgarner: In particular, imp_implementationWithBlock() takes a block as a parameter, copies it to the heap, and returns a trampoline that allows the block to be used as the implementation — the IMP — of a method in any Objective-C class (as long as the block’s arguments and the method’s arguments are compatible). [...]

  2. Pawel Solyga says:

    I just found a small typo, this:

    int(^doToThis_withThatIMP)(id s, NSString *n, double d);

    should be:

    int(^doToThis_withThatBLOCK)(id s, NSString *n, double d);

    [Ed: thanks. fixed.]

  3. Benedict's Soapbox » EMKAssociateDelegate says:

    [...] *block). I only found out about this function thanks to b.bum’s post iOS 4.3: imp_implementationWithBlock(). I’ve since tried to find the documentation for these function but have drawn a [...]

  4. Benedict Cohen says:

    Thanks for this.

    I’ve been trying to create a class that can use blocks for method implementations. With this code I’ve finally done it. I wrote a blog post about it:

    EMKAssociateDelegate

    Is there any documentation for this? I search ADC but came up blank.

  5. bbum’s weblog-o-mat» iOS 4.3: imp_implementationWithBlock() | Touchscreen Phone says:

    [...] bbum’s weblog-o-mat» iOS 4.3: imp_implementationWithBlock(): [...]

  6. John C. Randolph says:

    This is one of those clever things in Obj-C 2.0 that makes me want to find a reason to use them.

    -jcr

  7. Laurent says:

    I would love to use something along this line for a slightly different purpose. I’d like to forward an NSInvocation to a block. Think of it this way,
    I associate in an object a list of blocks (all with the correct signature) and when a specific message is sent to the object (which by default, does not implement said message) the forwardInvocation method is called with the NSInvocation of the missing message. I wish to walk down the list and forward the invocation to each closure (block). The above seems pretty close, except I don’t want to add a method to the NSInvocation to do it. Any suggestion/pointer?

  8. Steven Hepting says:

    Wow, this is really neat. The internals of Cocoa and Objective-C are constantly amazing me.

  9. Arcank says:

    Two typos:
    -(void)doSomething:
    should be
    -(void)doSomething; (semi colon)

    And the NSLog would be better outputting -[Answerer boogityBoo:3.141500] instead of [Answerer -boogityBoo:3.141500]

    Thanks for this article! Clear, with different examples :)

  10. Axel Roest says:

    the open curly bracket in the imp_implementationWithBlock block, does not seem to be closed. I copied and pasted it, but it doesn’t align.

    I guess it should come after the line with c * k);
    aka:

    IMP boo = imp_implementationWithBlock(^(id _self, float c) {
    NSLog(@"Executing [%@ -%@%f] %f",
    [_self class], NSStringFromSelector(_sel), c,
    c * k);
    });

  11. bob says:

    I don’t understand your explanation of what the trampoline does.

    The trampoline overwrites the second argument (_cmd) with the first argument (self), then shoves the reference to the block into the first argument slot, and finally tail calls to the block’s implementation.

    It seems that this would result in an extra argument. e.g. If we start with (self, _cmd, firstArg, secondArg, …), then your description would result in (block, self, firstArg, secondArg, …) instead of just (block, firstArg, secondArg, …), which is what you said the underlying block implementation takes: “a call to a Block is exactly like a C function with one argument at the beginning”.

  12. bob says:

    Oh nevermind (delete that last comment). I just saw that you must pass it a block with an initial object argument which then becomes self in the IMP.

    But then, what happens if you pass it a block without an initial object argument?

  13. Inside the Bracket, part 7 - Runtime Machinations » Big Nerd Ranch BlogBig Nerd Ranch Blog says:

    [...] The __bridge cast is to keep ARC happy because the function takes a void* for its block, presumably because there’s no common block type you’ll be passing. You get back a full-fledged IMP. implementationWithBlock wraps the block in a trampoline function and returns it to you. For more of the grungy details, check out Bill Bumgarner’s blog post. [...]

Leave a Reply

Line and paragraph breaks automatic.
XHTML allowed: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>