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
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,546评论 6 507
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,224评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,911评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,737评论 1 294
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,753评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,598评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,338评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,249评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,696评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,888评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,013评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,731评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,348评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,929评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,048评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,203评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,960评论 2 355

推荐阅读更多精彩内容

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