提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
一、概述
本文先从源码角度分析了runtime中的object,以及内存结构。然后从RetainCount方面介绍了如何进行管理内存,以及用autoreleasepool自动管理内存的原理,又分析了weak在内存管理的作用和原理。
二、深入runtime中的object
2.1 关于isa指针
所有继承于NSObject的对象有isa指针,其语意就是"is a",是isa指针指向的实例。在objc-private.h文件中objc_object结构体(objc-object.h文件实现)就标明了isa_t,如图2.1。所有继承自 NSObject 的类实例化后的对象都会包含一个类型为 isa_t 的结构体。
不只是实例会包含一个 isa 结构体,在objc-runtime-new.h文件中有个名为 objc_class 的结构体,它继承于objc_object,所以所有的类也有这么一个 isa,Class就是它的指针类型,如图2.2所示。
2.1.1 为什么需要isa指针?
- 语义上表示"is a",对象是什么。
- 因为在 Objective-C 中,对象的方法并没有存储于对象的结构体中(如果每一个对象都保存了自己能执行的方法,那么对内存的占用有极大的影响)。对象->类对象,类对象中存放实例方法。类对象->元类,元类中存放类方法。当方法被调用时,它要通过自己持有的 isa 来查找对应的类,然后在这里的 class_data_bits_t 结构体中查找对应方法的实现。如图2-3诠释了三者之间的关系。
2.1.2 isa的结构
一个isa指针占用64位,为了节省空间,每一位都有特殊的含义。在objc-private.h文件和isa.h文件中定义了isa_t共用体
在表2-1中,简要介绍了isa_t共用体在__x86_64__环境中,每一位的意义。
字段名 | 所占位数 | 字段说明 |
---|---|---|
nonpointer | 1 | 表示isa指针是否是个指针 |
has_assoc | 1 | 对象含有或者曾经含有关联引用,没有关联引用的可以更快地释放内存 |
has_cxx_dtor | 1 | 当前对象是否有 C++ 或者 ObjC 的析构器(destructor) |
shiftcls | 44 | 在为 indexed、 magic 和 has_cxx_dtor 设置之后,我们就要将当前对象对应的类指针存入 isa 结构体中了。 |
magic | 6 | 0x3b 用于调试器判断当前对象是真的对象还是没有初始化的空间 |
weakly_referenced | 1 | 对象是否有ARC 的弱引用 |
deallocating | 1 | 对象正在释放内存 |
has_sidetable_rc | 1 | 是否有扩展的retainCount用SideTable来存储 |
extra_rc | 8 | extra_rc = 引用计数 + 1 |
2.2 结构体objc_class
在objc-runtime-new.h中有objc_class结构体,不管是NSObject的初始化还是方法的被调用,都与之密切相关,objc_class继承objc_object
- isa是指向元类的指针。
- super_class 指向当前类的父类
- cache用于缓存指针和vtable,加速方法的调用
- bits就是64位的结构体,指向存储类的方法、属性、遵循的协议等信息的地方
2.2.1 class_data_bits_t 结构体
class_data_bits_t 的结构体,只含有64位的bits,用于存储与类有关的信息,其实就相当于相当于 class_rw_t 指针加上 rr/alloc 的标志(指针+标志位)
bits意义是class_rw_t指针加标志位,不同位意义如下
标志 | 占位数 | 字段说明 |
---|---|---|
isSwift | 1 | FAST_IS_SWIFT 用于判断 Swift 类 |
hasDefaultRR | 1 | FAST_HAS_DEFAULT_RR 当前类或者父类含有默认的 retain/release/autorelease/retainCount/_tryRetain/_isDeallocating/retainWeakReference/allowsWeakReference 方法 |
requiresRawIsa | 1 | FAST_REQUIRES_RAW_ISA 当前类的实例需要 raw isa |
class_rw_t | 47 | 是class_rw_t的指针,用于指向真正存储方法、属性、协议的地方。FAST_DATA_MASK就是取data的掩码。 |
2.2.2 class_rw_t结构体
OBJC类的属性、方法、协议等都保存在class_rw_t中
其中class_rw_t中的flags成员中比较重要的位域(objc-runtime-new.h)定义如下:
标志 | 占位数 | 字段说明 |
---|---|---|
RW_REALIZED | 1 | 类是已经注册的类 |
RW_FUTURE | 1 | 类是尚未解析的future class |
RW_INITIALIZED | 1 | 类是已经初始化的类 |
RW_INITIALIZING | 1 | 类是正在初始化的类 |
RW_COPIED_RO | 1 | class_rw_t->ro是class_ro_t的堆拷贝,此时类的class_rw_t->ro是可写入的,拷贝之前ro的内存区域锁死不可写入 |
RW_CONSTRUCTING | 1 | 类是正在构建而仍未注册的类 |
RW_CONSTRUCTED | 1 | 类是已经构建完成并注册的类 |
RW_LOADED | 1 | 类是load方法已经调用过的类 |
2.2.3 class_ro_t结构体
class_ro_t存储了当前类在编译期就已经确定的属性、方法以及遵循的协议,如图2.8所示。
变量名 | 备注 |
---|---|
baseMethodList | 直接写在Class中的方法列表 |
baseProtocols | Class直接继承的协议列表 |
baseProperties | Class直接写的@property |
ivars | 直接写在Class的实例变量 |
instanceStart | 本类偏移所有父类的字节数 |
instanceSize | 本类所有实例变量大小的和 |
其中class_ro_t中的flags成员中比较重要的位域(objc-runtime-new.h)定义如下:
标志 | 占位数 | 字段说明 |
---|---|---|
RO_META | 1 | 类是元类 |
RO_ROOT | 1 | 类是根类 |
RO_HAS_CXX_STRUCTORS | 1 | 类有C++构造/析构函数 |
RO_HAS_LOAD_METHOD | 1 | 类有实现load方法 |
RO_HIDDEN | 1 | 隐藏类 |
RO_IS_ARC | 1 | 类使用ARC选项编译 |
RO_HAS_CXX_DTOR_ONLY | 1 | 类有C++析构函数,但没有C++构造函数 |
RO_FORBIDS_ASSOCIATED_OBJECTS | 1 | 类禁止使用关联对象 |
RO_FUTURE | 1 | 类是尚未解析的future class |
RO_REALIZED | 1 | 类是已经注册的类 |
2.2.4 objc_class加载的真正过程
- 在编译期间类的结构中的 class_data_bits_t *data 指向的是一个 class_ro_t * 指针。
- 在runtime期间,执行realizeClassWithoutSwift方法中:
- 将结果从 class_rw_t 强制转换为 class_ro_t 指针
- 初始化一个 class_rw_t 结构体
- 设置结构体 ro 的值以及 flag
- 把rw设置成Class的正确data
执行完realizeClassWithoutSwift类所占用内存的布局如图
- realizeClassWithoutSwift的最后调用methodizeClass。
- 将ro中的methods、properties、protocols都加到rw中,
- 将本Class的未绑定的Categories的methods、properties、protocols都加到rw中。