基情四射的日子
我总是对神秘的事物产生兴趣,自从知道了Objective-C的Runtime技能,我便试着接近它,讨好它,因为只有对这个技能了如指掌,我才能征服它,才能在修仙的道路上顺利前进。
消息传递函数: objc_msgSend
这个可以说是Runtime系统的根基,因为所有的OC方法调用最终都会转换成objc_msgSend函数的调用。这么重要的秘密,没想到只给丫喝了两瓶啤的,它就告诉了我。看来酒量不行,难在江湖混呐。
招式破解:[receiver message] ----> objc_msgSend(receiver, selector, arg1, arg2, ...) objc_msgSend在执行的过程中,会悄悄的传入接收对象和方法选择器这两个参数。
看了Runtime醉酒的样子,我心里嘀咕着:“就这货,怎么能有这么强大的力量?”果然不出我所料,这货有一枚神器叫编译器,一直在默默的帮助着它。就这个消息传递,也是因为编译器为每一个类和对象构建了各自的结构,才能使其顺利施放技能。那我倒是要看看,这个类结构到底是啥?
类结构破解:
类结构包含了一个指向父类的指针(isa)和一个类调度表。类调度表记录了类中选择器对应的具体内存地址,就像这样:selector ----> address 。当调用一个方法,也就是给对象发送一个消息的时候,会根据isa找到类结构,并在调度表中查找匹配的selector;如果不能匹配,objc_msgSend就根据isa到父类类结构里与调度表中的selector匹配,依此类推直到NSObject类。一旦匹配到了selector,就调用表中记录的函数(address),否则将启动消息转发机制。这种在运行时选择方法实现的过程称为动态绑定。
强大的Runtime必然会有很多的招式,这招动态绑定,我给90分。因为我觉着这样灵活的招式,肯定会有它的弊端。首先在编译期不能确定方法的位置,这就给运行时造成了不可避免的麻烦,从而导致效率上的问题。哼哼~果然被我找到了弱点。但是后来的一番话,差点迷惑了我。
效率提升招式之缓存:
每个类都有单独的缓存,包含了继承的selector和类中定义的selector(只有在曾今调用过的方法才会进行缓存)。在匹配调度表之前,先检查接收对象的类缓存(哈希散列算法),如果缓存中有匹配的selector,就直接使用。这大大减少了运行时查询selector所消耗的时间。
我差点就信了。后来仔细一想,如果我连续多次调用一个特定的方法,那不就玩完了吗?哈哈哈,丫果然有弱点,看我怎么支配你。
我也算是武学奇才,一会儿就研究了一个招式:规避动态绑定机制。
首先我要想办法得到一个方法的内存地址,然后就直接调用啊,这不就没有查找selector的过程了吗。至于怎么得到方法的地址,就要靠methodForSelector方法了。请看下面的招式玩耍:
void (*showTitle)(id, SEL, NSString *);
int i;
showTitle = (void(*)(id, SEL, NSString *))[target methodForSelector:@selector(showMessage:)];
for (i=0; i<1000; ++i) {
showTitle(targetList[i], @selector(showMessage:), "邪魔退散");
}
这样在重复多次调用一个特定的方法,有显著的效率提升。
学会了这招技能,我看着熟睡的Runtime,心想:“总有一天,在你遇到问题的时候,我会助你一臂之力的。”
不会吧,我竟然对它没有了敌意。看来在修仙的道路上,我会多一位兄弟(jiyou)了。
关注微信公众号CodingArtist,可以第一时间得到文章更新通知! ^_^