Objective-C runtime - 类和对象
前言
OC是一门面向对象的语言,这篇文章先来看看OC的类和对象(Class && Object)
正文
- OC中的对象本质上是一个objc_object结构体
- OC中的类本质上是一个objc_class结构体
- id:指向objc_object的结构体指针
- Class:指向objc_class的结构体指针
objc_class 和 objc_object
先来看看两个结构体的结构(先只考虑结构体的成员变量,不考虑它的成员函数)
1 | struct objc_object { |
objc_object 定义在 objc-private.h
里面,该结构体只包含一个叫做 isa 的isa_t类型的变量
objc_class 定义在 objc-runtime-new.h
里面,它继承自objc_object,所以除了isa成员变量之外,它还有:
- 指向另一个objc_class结构体的指针 super_class;这一个objc_class包含了父类的信息
- 一个包含函数缓存的cache_t类型变量 cache
- 一个包含类的方法,属性等信息的class_data_bits_t类型的变量 bits
编译结束之后,OC的每个类都是以objc_class的结构体形式存在于内存之中,而且在内存中的位置已经固定。运行期间,创建新的对象的时候,也是创建objc_object的结构体
isa
成员变量isa包含了该对象,或者该类的信息。isa变量的类型是isa_t,定义在objc_private.h
1 | union isa_t |
ps : 以上代码是在x86_64上的实现(Mac上面);iPhone(arm64等)上面的实现大同小异。具体实现可以参见runtime源码
has_assoc
表示该对象是否有关联对象
has_cxx_dtor
表示是否有析构函数
shiftcls
表示对象所属的类或者类所属的元类(meta class)的地址,也就是指向一个objc_class的指针,不过该指针只有44位。
64位系统的指针占用64bit的内存,但是使用整个指针大小来存储地址有点浪费。在mac的64位系统上面,使用47位作为指针,其他的17位用于其他目的(iPhone上面只使用33位)。又由于所有对象按照8字节对齐,所以指针都是能被8整除的,也就是后3bit均为0;所以类指针的实际有效位数为 47 - 3 = 44
位。这也是shiftcls只有44位的原因
isa初始化
在创建新的objc_object的时候,会使用objc_object的成员函数initIsa
来初始化isa变量
1 | inline void |
这里的indexed用来标记isa是否有结构体部分:
- 如果indexed为0,没有结构体部分,直接将对象所属的类的地址赋予cls变量
- 如果indexed为1,有结构体部分,先使用
ISA_MAGIC_VALUE
给isa这个64位union初始化,然后在对64位上的每一位单独设置。比如设置是否含有析构函数(has_cxx_dtor),设置对象所属类的地址(shiftcls)。注意这里设置类地址的时候是将cls右移3位,再赋值给shiftcls的,原因就是类地址的最后3bit没有实际作用
isa()方法
该方法用来获取对象所属的类的指针,也就是表示该对象是一个什么类
1 | inline Class |
返回的是一个64位的指向objc_class结构体的指针,其中的4-47bit为shiftcls的值,其他bit都是0
总结
OC的对象和类在内存中都是以结构体的形式存在的,类对应objc_class结构体,对象对应objc_object结构体。
在创建这两种结构体的过程中都会初始化isa变量。isa变量存放着对象所属的类的信息,或者类所属的元类的信息。关于元类的相关内容,可自行google。
另外,在创建objc_class的结构体的时候还会初始化相应的cache和bits变量,在后续的章节中将会说道