ObjC是一门动态性比较强的编程语言,而这一系列的动态性就是依赖于Runtime来支撑的。算法
在iPhone cpu 使用arm64架构以前,isa指针就是一个Class类型的普通指针,存储着Class、Meta-Class的内存地址。在这以后apple对isa指针进行了优化,利用位域的技术,将isa指针换成了一个union(共用体)。arm64架构以后的isa指针每一位都存储着不一样的信息,在isa的union中有一个结构体表示了每一位对应的含义。编程
在runtime源码中class的定义为:api
其中咱们能够看到经过bits的data()
函数能够获取到一个class_rw_t
的结构体指针。class_rw_t
结构体中存放着咱们熟悉的东西:方法列表、属性列表和协议列表等。除此以外还有一个class_ro_t
的结构体指针,它指向的结构体拥有与class_rw_t
结构体相似的结构,也存放有方法列表、协议列表,初次以外还有成员变量列表等数据。数组
经过class_rw_t
与class_ro_t
的名字咱们能够看出,rw表明的是可读可写的结构,而ro则表明只读的结构。缓存
整体上说class的结构能够用下图表示架构
在class_rw_t
中方法列表、属性列表、协议列表都是xxx_array_t
二维数组,而class_ro_t
中的方法列表,协议列表等都是xxx_list_t
的一维数组。class_rw_t
中的二维数组中的存放的就是许多xxx_list_t
的一维数组。这些一维数组多是来自原始类(class_ro_t
中存放的是类的原始的信息),也多是来自category。app
对于methodlist而言,一个方法就是method_list_t
的一个元素,其关系能够用下图表示:编程语言
也就是说method_t
才方法列表中存储的最小元素。method_t的是一个结构,它是对方法/函数的封装。函数
method_t
有方法名称(name)、返回值类型和参数类型的编码(types)、指向函数的指针(imp)。优化
name 就是方法的名称,它是SEL
类型的。SEL
就是selector是方法选择器,它是根据方法名字生成的,本质上就是一个字符串,跟方法存放的位置无关。也就是说不一样类中相同名字的方法的方法选择器是同样的。
获取SEL
的方法可使用@selector()
或者runtime api中的sel_registerName()
得到。使用sel_getName()
或者NSStringFromSelector()
将其转换为字符串。
types 包含了函数返回值和参数编码的字符串。它就是一个字符串,将函数返回值类型和参数类型按照必定的编码规则生成的一个字符串。可使用@encode()
将具体类型表示成字符串编码。
apple提供的编码规则以下:
imp在runtime中的定义以下:
正如其注释所说:这是一个指向方法实现的函数指针。
咱们知道平时咱们进行方法调用的时候不可能一直按照从isa指针开查找方法的规则来查找方法的实现的。runtime定义的类中有一个方法缓存,这里面缓存了调用过的方法。当咱们调用方法的时候runtime会首先从缓存中查找有没有方法的实现被缓存过。如如有,则直接经过缓存取出方法进行调用,若是没有则再按照isa指针的流程进行方法实现的查找。
方法缓存是使用散列表实现的。SEL做为散列表的key,IMP和SEL做为散列表的value。
经过上图咱们也能够看出_buckets
是一个数组,数组中存放了方法缓存的散列表。那么是如何从方法缓存中取出方法来的呢?若是是遍历查找的话,效率是很是低的,apple利用了一种算法,它利用@selector(method_name)
和mask(buckets的长度减一)进行一次按位与运算得出一个下标,而后将方法缓存存放到这个下标对应的位置,一样取缓存的时候也是进行了这样一次操做。这样一来效率就提升了,可是整个数组中会出现一些没有用到的空位置,因此这是一种利用空间来换时间的算法。
本篇简单的介绍了Runtime中关于isa、class、以及方法缓存的相关问题。下篇介绍obj方法调用的相关问题。