最近在研究Runtime,因此,打算写一篇文章跟小伙伴儿们分享一下。好了,废话不多说,直接上干货。
RunTime简称运行时。OC就是运行时机制,也就是在运行时候的一些机制,其中最主要的是消息机制。简单说一下C与OC在编译和运行阶段的区别,对于C语言,函数的调用在编译的时候会决定调用哪个函数。对于OC的函数,属于动态调用过程,在编译的时候并不能决定真正调用哪个函数,只有在真正运行的时候才会根据函数的名称找到对应的函数来调用。
Runtime有5大作用:发送消息,交换方法,动态添加方法,给分类添加属性,字典转模型,下面一一给大家讲解一下这5个作用。
一、发送消息
任何方法调用的本质就是发送一个消息,用runtime发送消息,OC底层就是通过runtime实现的。下面给大家展示一下底层的代码:
正常的OC代码通过Xcode的编译器Clang重新编译,就会生成底层的代码,也就是消息机制的代码。话说回来,怎么使用编译器重新编译呢?我们在终端输入clang -rewrite-objc main.m 就可以得到最终生成代码了。
我们使用Runtime时,必须要提前导入头文件<objc/message.h>,可能有人会问我,为什么不导入<objc/runtime.h>?因为我们进入message.h的声明中,会发现已经导入了runtime.h。
上面展示的代码是最底层的代码,写着太麻烦了,很少用,下面给大家展示一下我们平常写的代码:
这个就是我们平常写的,第一个参数的意思是:谁发送消息 第二个参数的意思是:发送什么消息。
其实,还有一种写法,也是可以的:
上面仅仅给大家展示了一些消息机制底层代码的一下写法,下面说一下Runtime在消息机制中最重要的一个作用:“runtime消息机制,可以调用私有方法”!!!!!!
下面给大家展示一下,调用私有方法:
上面的eat,run方法在Person类中均没有声明,只有实现。
注:我们在用对象p调用方法时,不要用Person *p = objc_msgSend(object_getClass(@"Person"), sel_registerName("alloc"))这种形式,否则,会崩。
上面是对象方法,下面给大家展示一下类方法。
对象方法的对象调用,类方法的本质是类对象调用。
下面,给大家分享一下方法的调用流程:
1.去寻找对应的类对象,每一个对象都有一个isa指针,通过isa指针去对应类中查找;
2.注册方法编号
3.根据方法编号查找对应的方法
4.找到只是最终函数实现地址,根据地址去方法区调用对应函数。
二、交换方法
交换方法是Runtime中最常用的,我们在做项目时经常用到。
Runtime(交换方法):只要想修改系统的方法实现。
比如:有一个项目,已经开发了2年,忽然项目负责人添加一个功能,每次UIImage加载图片,告诉我是否加载成功?
这样的一个需求,除了使用Runtime交换方法,用其他的方法很难实现。
交换方法的步骤为:(1)给系统的方法添加分类;
(2)自己实现一个带有扩展功能的方法;
(3)交换方法的实现,只需要交换一次。
下面直接上代码:
分类的声明:
分类的实现:
三、动态添加方法
动态添加方法:OC是懒加载机制,只要一个方法实现了,就会马上添加到方法列表中(不管这个方法有没有用过,都会添加进去)。如果某个类中方法比较多,而且有很多方法不常用,需要给每个方法都生成映射表,加载类到内存的时候就比较耗费资源,可以使用动态给该类添加方法解决。
下面直接上代码:
Person类的实现部分:
动态添加方法在做项目时用得比较少。
4、动态添加属性
我们给系统的类添加属性的时候,可以使用runtime动态添加属性。动态添加属性的本质:让某个属性和某个对象产生一个关联,并不是直接把这个值的内存空间添加到类内存空间。
代码如下:
给系统的类添加一个分类,声明部分:
实现部分:
4、字典转模型
字典转模型有两种方法:1.KVC 2.Runtime。第三方框架MJExtension底层就是用Runtime字典转模型的。
KVC的实现原理是:遍历字典中所有的key,去模型中查找对应的属性赋值;Runtime实现原理刚好与KVC相反:通过runtime,把一个模型中所有属性遍历出来,根据属性去字典里面找。
我们可以创建一个NSObject分类,专门字典转模型,以后所有模型都可以通过这个分类转
代码如下: