imp_implementationWithBlock()的实现机制

本文为大地瓜原创,欢迎知识共享,转载请注明出处。
虽然你不注明出处我也没什么精力和你计较。
作者微信号:christgreenlaw


本文的原文。本文只对其进行翻译。


iOS 4.3中,有三个非常底层的runtime函数提供了一种新的OC和Blocks之间的桥梁,其目的是加强方法实现的动态生成(facilitating the dynamic generation of method implementations)。

具体一点来说:

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

需要特别指明的是,imp_implementationWithBlock():接收一个block参数,将其拷贝到堆中,返回一个trampoline(直译为“蹦床”,大家意会吧),可以让block当做OC任何一个类的方法的实现(implementation)--IMP--来使用(一个大前提是block的参数和方法的参数时匹配的)。

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

这里skewIMP会包含一个函数指针,可以作为下面这样声明的方法的IMP:

- (int)skew: (int) k;

需要注意的是:imp_implementationWithBlock()不返回一个可以像function一样调用的block的函数指针。其中的关键是:IMP总是最少有两个参数:(id self, SEL _cmd)

要声明一个block,你要丢弃掉SEL _cmd,保留其它参数。

就像这样:

-(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);

这种模式的做法和OC以及Blocks的ABI(Application Binary Interface,应用程序二进制接口)是有关系的。method其实就是开头带有两个参数的C function:收到消息的object以及正在执行的方法的selector。与之相似的是,调用Block就像开头带有一个参数的C function:一个block的引用(as described in the Block ABI on the llvm.org site)。

在我之前写的 intimate tour of objc_msgSend()中我说过, 想让objc_msgSend运行的更快,有以下几个要求或者优化方式:

  • 除非必要,不要碰registers
  • 优化tail call
  • 不要碰参数列表

imp_implementationWithBlock()也是一样的,返回的函数指针尽可能少地修改参数列表,然后调用block的实现。因此,它速度快,广泛适用于方法实现。

关键就是,方法实现总是在参数列表起始处有两个指针参数:self & _cmd。trampoline用第一个参数(self)重写第二个参数(_cmd)。将block 的引用放入第一个参数,最后调用block 的实现。

更重要的是,imp_implementationWithBlock()所返回的函数指针--IMP--和别的IMP没有区别。它可以被传递给任何接受IMP参数的API,传递给class_getMethod()type string 和一个常规的 “编译期IMP”没有什么区别。

没了吗?

block在触发时,自身作为第一个参数,其他的参数,不管有多少,在这个过程中都留着不动。

另外两个函数——imp_getBlock()imp_removeBlock()——是为了完整性而提供的。很显然,移除或销毁一个方法的当前的IMP是应用中快速结束的好方式。

总结一下

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
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 173,822评论 25 709
  • 本文为大地瓜原创,欢迎知识共享,转载请注明出处。虽然你不注明出处我也没什么精力和你计较。作者微信号:christg...
    大地瓜123阅读 454评论 0 1
  • 【在简书的第7首诗 】 冬天脱掉鞋子 踮着脚小心翼翼 踩到秋丰腴的尾巴 单薄的身体 皑皑白雪还在赶来的路上 萧瑟的...
    熹原阅读 302评论 0 3
  • 《人面桃花》 阳光灿烂春花开 柳绿草青天蓝蓝 燕子飞舞莺歌唱 这画面太美太醉人 我却走不进这春光 又是一年花落尽 ...
    晚熟的柿子阅读 301评论 0 0
  • 过生日,想必对我们来说已不再陌生。那一天,有歌,有酒,有朋友。我们听着朋友的祝福应和着,拿着朋友的礼物微笑着,...
    7958e590248c阅读 948评论 0 1