1.runtime(运行时机制)是什么?
runtime是属于OC的底层,是一套比较底层的纯C语言API, 属于1个C语言库, 包含了很多底层的C语言API,可以进行一些非常底层的操作(用OC是无法现实的, 不好实现)。 在我们平时编写的OC代码中, 程序运行过程时, 其实最终都是转成了runtime的C语言代码, runtime算是OC的幕后工作者,runtime可以让我们更好的理解OC这门语言
2.runtime的基本理解
Class类的本质就是一个结构体指针类型,其内部结构如下所示:
![](https://img.haomeiwen.com/i8957764/aae6a8782a35ff6f.png)
Class 也有一个 isa 指针,指向其所属的元类(metaClass)。
·super_class:指向其超类。
·name:是类名。
·version:是类的版本信息。
·info:是类的详情。
·instance_size:是该类的实例对象的大小。
·ivars:指向该类的成员变量列表。
·methodLists:指向该类的实例方法列表,它将方法选择器和方法实现地址联系起来。methodLists 是指向 ·objc_method_list 指针的指针,也就是说可以动态修改 *methodLists 的值来添加成员方法,而这里就是Categeory的工作原理,同时也解释了Categeory不能添加属性。
·cache:Runtime 系统会把被调用的方法存到 cache 中(理论上讲一个方法如果被调用,那么它有可能今后还会被调用),下次查找的时候效率更高。
·protocols:指向该类的协议列表
说了这么多的抽象的东西,可能大家有点难以理解,那么方法调用是如何一步一步工作起来的呢?我们可以应用clang命令来查看OC转为C的代码看下大概,比如下面的代码:
![](https://img.haomeiwen.com/i8957764/ccef2031fe312db9.png)
使用clang命令转为cpp文件如下:
![](https://img.haomeiwen.com/i8957764/0ccd057f817fd47a.png)
Cell.cpp里面展示了方法后面的内部实现,可以看到方法调用的本质是发送消息[reciever function]被转换成objc_msgSend(reciever,selector);看一个cell初始化里面做了什么事情:
![](https://img.haomeiwen.com/i8957764/432bf5662c33038a.png)
[self addsubView]被转换成了((void(*)(id, SEL, UIView *))(void*)objc_msgSend)((id)((UIView *(*)(id, SEL))(void*)objc_msgSend)((id)self, sel_registerName("contentView")), sel_registerName("addSubview:"), (UIView *)(*(UIButton **)((char*)self + OBJC_IVAR_$_Cell$_check)));
objc_msgSend 函数被定义在 objc/message.h 目录下,其函数原型是这样的:
OBJC_EXPORT void objc_msgSend(void/* id self, SEL op, ... */) 这就是方法调用的本质。
当我们调用一个方法时,大致经历了以下过程:
首先,Runtime 系统会把方法调用转化为消息发送,即 objc_msgSend,并且把方法的调用者,和方法选择器,当做参数传递过去,此时,方法的调用者会通过 isa 指针来找到其所属的类,然后在 cache 或者 methodLists 中查找该方法,找得到就跳到对应的方法去执行。如果在类中没有找到该方法,则通过 super_class 往上一级超类查找 这也就是为什么继承了一个类并重写父类方法会首先执行重写的方法而不是执行父类方法了(如果一直找到 NSObject 都没有找到该方法的话,这种情况,就会报错unrecognized selector sent to instance xxxxx。
前面我们说 methodLists 指向该类的实例方法列表,实例方法即-方法,那么类方法(+方法)存储在哪儿呢?类方法被存储在元类中,Class 通过 isa 指针即可找到其所属的元类。
![](https://img.haomeiwen.com/i8957764/ef37399d0aed4f88.jpg)
我们再看下这段代码:
objc_msgSendSuper)((__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass("Cell"))}, sel_registerName("initWithStyle:reuseIdentifier:"), (UITableViewCellStyle)style, (NSString *_Nullable)reuseIdentifier);
这是调用super init...方法.当我们调用 [super selector] 时,Runtime 会调用 objc_msgSendSuper 方法,objc_msgSendSuper 方法有两个参数,super 和 op,Runtime 会把 selector 方法选择器赋值给 op。而 super 是一个 objc_super 结构体指针,这就是调用super方法runtime做的事情.看到这里,是不是对OC的了解好像是更进了一步??
3.runtime的应用都有哪些?
1.给对象关联属性
像给UIViewController关联一个可变数组用以加网络操作:
![](https://img.haomeiwen.com/i8957764/9d3dc6ba6fb3eb1d.png)
2.归档和解归档
以前的model属性要实现归档和解归档都是这样写的:
![](https://img.haomeiwen.com/i8957764/50f9624e746ecfa5.png)
可是问题来了,要是一个class有100个属性呢?!,于是runtime可以很好的帮助你获取到一个class的所有成员变量,如下所示:
![](https://img.haomeiwen.com/i8957764/1e286e1b122101e1.png)
这也就是著名的三方框架:MJExtendsion的核心思想,MJExtendsion就是大量应用了runtime来获取类的所有属性来进行操作.
3.model和json之间的互相转换,大家可以去了解下MJExtendsion的源代码.
4.方法交换,动态方法解析(慎用).
网友评论