前言
几个数据结构概念
objc_object
objc_class
objc_class继承于objc_object。所以在objc_class中也会包含isa_t类型的结构体isa。至此,可以得出结论:Objective-C 中类也是一个对象。在objc_class中,除了isa之外,还有3个成员变量,一个是父类的指针,一个是方法缓存,最后一个这个类的实例方法链表。
isa指针
什么作用呢?
注意:
isa指针:
是一个共用体类型。
一般是32位或者64位。
分为两种:
1)指针型isa:代表Class的地址。
2)非指针型isa:比如64位其中的30+、40+位代表Class地址,就可以寻找到所有Class,就够用了。其余位则表示其它相关内容达到节省内存的目的。这也是有两种isa的初衷。
isa指向
注意:
1)关于对象,也就是实例,在runtime中对应于objc_object,它其中有一个isa指针,指向对应的类。
2)关于类对象,Class代表objc_Class,继承objc_object,自然也是有isa指针,指向其元类对象MetaClass。
所以,在进行方法调用的时候,实例会通过isa指针就会去类中进行查找;类对象则会通过isa指针到它的元类方法中进行方法查找。
对象 类 元类之间关系
-
图中实线是 super_class指针,虚线是isa指针。
-
Root class (class)其实就是NSObject,NSObject是没有超类的,所以Root class(class)的superclass指向nil。
-
每个Class都有一个isa指针指向唯一的Meta class
-
Root class(meta)的superclass指向Root class(class),也就是NSObject,形成一个回路。
-
每个Meta class的isa指针都指向Root class (meta)。
cache_t
cache_t是一个数组,每个元素都是一个bucket_t,其中包含两个成员变量,一个是key,即selector;另外一个IMP是一个无类型的函数指针。
比如给出一个key,就可以通过哈希查找算法定位key对应的这个key位于数组当中的位置,然后通过提取IMP来调用函数。
Cache的作用主要是为了优化方法调用的性能。当对象receiver调用方法message时,首先根据对象receiver的isa指针查找到它对应的类,然后在类的methodLists中搜索方法,如果没有找到,就使用super_class指针到父类中的methodLists查找,一旦找到就调用方法。如果没有找到,有可能消息转发,也可能忽略它。但这样查找方式效率太低,因为往往一个类大概只有20%的方法经常被调用,占总调用次数的80%。所以使用Cache来缓存经常调用的方法,当调用方法时,优先在Cache查找,如果没有找到,再到methodLists查找。
注意:局部性原理
在调用方法的时候,往往调用的都是调用频次最高的,这时把这些方法放在缓存中,下次命中的概率会高一些。
class_data_bits_t
是objc_class中的结构。
class_rw_t代表了类相关的读写信息,比如关于给类添加的一些分类方法、属性、协议,也对class_ro_t进行了封装。
class_ro_t代表了类相关的只读信息
class_rw_t
class_ro_t
methodList是一维数组(由于该结构是只读的,不允许添加新元素),所以存放的是原有的一些方法。
注意:
-
类在内存中的位置是在编译期间决定的,在之后修改代码,也不会改变内存中的位置。
-
类的方法、属性以及协议在编译期间存放到了“错误”的位置,直到 realizeClass 执行之后,才放到了 class_rw_t 指向的只读区域 class_ro_t,这样我们即可以在运行时为 class_rw_t 添加方法,也不会影响类的只读结构。
-
在 class_ro_t 中的属性在运行期间就不能改变了,再添加方法时,会修改 class_rw_t 中的 methods 列表,而不是 class_ro_t 中的 baseMethods,对于方法的添加会在之后的文章中分析。
method_t
types Encoding技术
下面代码第一项和第二项参数对应上图的id 和 SEL
id objc_msgSend(id self, SEL op, ...);
总结:
参考链接:
神经病院 Objective-C Runtime 入院第一天—— isa 和 Class
从 NSObject 的初始化了解 isa
Runtime数据结构相关面试题
网友评论