三 、动态添加方法缓存
咱们能够经过runtime动态地添加方法。那么到底啥叫动态添加方法呢?动态添加方法就是当咱们程序运行时才知道咱们应该调用哪一个方法。咱们首先须要了解这一点,当咱们编写完一段代码后,咱们点击run 的时候,编译器会先进行预编译、编译、连接、运行这几个步骤。C语言是再编译的时候就已经肯定了函数的调用顺序,而OC在编译的时候,只是肯定了哪一个对象,发送什么消息,具体这个消息能不能找到对应的方法还不知道,只有在运行时,才能肯定是否可以执行咱们所指望的方法。咱们如下面的代码为例:函数
[person test_inPerson]; // objc_msgSend(person, @selector(test_inPerson));上面的语句再编译后获得的就是这个函数。 // 经过编译咱们可以知道三点:一、消息的接受者是person对象。二、须要执行名字为test_inPerson 的方法 三、这个方法不带参数
在上一篇博客中,我提到了SEL和IMP,可是因为写博客时已经太晚了,就没有认真地解释。spa
若是想了解方法调用的过程,恐怕咱们须要了解4个概念:(1)isa 指针。( 2)superclass 属性 。 (3)SEL 。 ( 4)IMP。指针
1) isa 指针:指向对象的类的指针。code
2)superclass :指向父类。orm
3)SEL :选择器,是根据方法名字生成的ID, 每一个selector实际上是一个char*类型,记录对应IMP的位置。SEL列表自己是一个哈希存储的set集合,查找起来很是高效。对象
4) IMP:函数指针。编译器
下面咱们就来讲一下方法调用的过程。博客
/* 调用方法 */ [person test_inPerson]; /* 转换成消息 objc_msgSend(person, @selector(test_inPerson));上面的语句再编译后获得的就是这个函数。 */ /* 1)检查 是否selector 2)检查person 是否为空,若是为空的话就把selector也置为空,这样的话至关于什么也不作,固然也不报错 3)根据SEL 查找IMP。首先从缓存中查找,看看缓存中是否存在SEL对应的IMP。若是存在则执行,不然继续下一步。 4)根据SEL 和 isa 指针再IMP 列表中查找对应的IMP。若是找到则执行,不然执行下一步。 5)根据superclass 和SEL 查找父类的IMP 若是找到则执行。不然继续执行这一步,直到NSObject 类。 6)若是再NSObject类中仍然找不到方法,则会报错,找不到方法。 */
了解了方法调用的过程,下面咱们就来看看如何动态的添加方法。为了可以表达清楚,特在此敬上代码it
DZLPerson *person=[[DZLPerson alloc] init]; //发送消息想要执行名字为test0的方法,可是咱们person类及其分类中并无该方法的实现 [person performSelector:@selector(test0)];
#import "DZLPerson.h" #import <objc/runtime.h> @implementation DZLPerson /* 注意 这是函数 不是方法。函数是不能经过方法调用的。 */ void test0() { NSLog(@"test0 执行了"); } /* 若是找不到类方法则调用该方法,决定是否动态地添加方法 */ //+(BOOL)resolveClassMethod:(SEL)sel //{ // return BOOL; //} /* 若是找不到实例方法则调用该方法,决定是否动态地添加方法 */ +(BOOL)resolveInstanceMethod:(SEL)sel { // 若是找不到的方法时test0 的话 if ([NSStringFromSelector(sel) isEqualToString:@"test0"]) { //添加方法。其实就是将现有的函数实现(IMP) 和 SEL进行链接。 class_addMethod(self,sel,test0,"v@:"); } return YES; } @end
打印结果为
2015-04-13 22:43:06.406 runtime讲解[12452:693059] test0执行了
说明咱们动态添加方法成功了。最后特别提示哦,若是函数和方法不是一回事,不要把他俩搞混了。方法是经过类或者对象调用的,而函数是能够直接调用执行的。