iOS底层学习第一节:OC对象探索
一、OC对象alloc & init 做了什么?
众所周知alloc是开辟内存,init是初始化,我们查看一下下图,p1,p2,p3三个对象为同一个内存空间,指针p1,p2,p3为不同的指针,一个指针8个字节。
init是一个默认构造方法,我们可以复写它而提供不同的构造方法初始化对象,alloc是一个开辟内存的方法,通过debug我们可以查看调用栈发现它进入了一个objc_alloc方法,但是我们无法查看源码,这时候需要去网上下载runtime源码。
二、查看alloc流程
首先我们可以下载runtime源码并且让这个项目运行起来,KC老师就是厉害...好好学习,赶上他的一半。
参考掘金:https://juejin.cn/post/6914265121183236104
runtime源码下载地址:https://opensource.apple.com/tarballs/objc4/
NSObjcet初始化流程: objc_alloc -> callAlloc -> _objc_rootAllocWithZone -> _class_createInstanceFromeZone
Person初始化流程:objc_alloc -> callAlloc -> alloc ->_objc_rootAlloc ->callAlloc ->_objc_rootAllocWithZone -> _class_createInstanceFromeZone
这个流程调用栈其实并不重要,重要的是_class_createInstanceFromeZone方法中的三个方法分别是:计算对象内存大小,开辟对象内存大小,绑定对象的isa
引入一个问题:为什么NSObjcet没有走alloc方法,而Person走了alloc->_objc_rootAlloc方法?
解答:注意细节fastpath(!cls->ISA()->hasCustomAWZ())这个判断语句,如果存在isa,则直接走_objc_rootAllocWithZone,这个时候NSObjcet是存在isa的(之前就已经被初始化过,因为是所有的对象的父类);猜测第一次alloc的对象去自己的_objc_rootAlloc方法会有一些奇怪操作,之后重复调用alloc会去判断fastpath(!cls->ISA()->hasCustomAWZ())(会在LLVM中有这部分优化操作导致这个判断是true),然后直接去_objc_rootAllocWithZone初始化
三、_class_createInstanceFromeZone三个方法的作用
(网上找的图,侵删)
前文可知三个方法分别为计算对象内存大小,开辟对象内存大小,绑定对象isa
说到isa,这里拓展一个objc_class(objc1使用,现在已经废除),如下图,所有的对象都是结构体,对象的结构体objc_object只有isa和属性,类和元类都是使用objc_class这个结构体
- isa指向类控件
- super_class是父类class
- name,version,info就是一些信息
- instance_size是对象的大小
- ivars是属性列表
- methodLists是方法列表
- cache是已经缓存的方法地址
- protocols是协议列表
instanceSize方法
初始化内存大小,引入内存对齐概念:在iOS中,对象的最小单位为16字节,即使只存在isa(8字节)后面的8字节为空,这是因为macOS系统中读取是按照块来读,一块为16字节(系统升级提高效率),如果对象最小单位为8字节,则会读到其他对象内存,容易造成混乱。
首先我们看一下结构体对齐的三个条件,结构体中如果属性顺序不同,相同属性的结构体可能最终需要的内存不一样。
前面我们说过对象就是一个结构体,然而我们在写OC对象的时候却没有考虑属性的顺序,这是因为Clang在进行代码优化的时候直接选择了最优的结构体顺序(博客Clang与LLVM的关系 https://blog.csdn.net/baidu_24256693/article/details/79905396 )
calloc方法
开辟内存空间
initInstanceIsa方法
类对象和对象isa绑定
四、isa介绍
联合体位域
前面我们提过isa只有8字节,也就是64位,但是这个isa里面却存放了很多的信息,使用位的01来表示BOOL的true,false,这就是联合体位域(我之前学习的时候有位图bitmap的叫法),在arm_64位系统的参考下,isa结构体定义如图
简单介绍一下这里面的参数,其实有很多我也不认识...
ISA_MASK:前面我们介绍,isa是一个联合体位域,只有中间33位才是真实的class对象,我们取出来对象的isa,然后& ISA_MASK就可以获得 Class 信息,如图我们调用类方法 getClass其实也就是取出来bits,然后&ISA_MASK
Nonpointer:是否开启isa指针优化0代表纯isa指针,1代表isa优化
Has_assoc:是否存在关联对象,释放内存不存在关联对象不需要释放减少消耗
Has_xx_dtor:是否存在析构方法,释放内存不存在析构方法不调用减少消耗
Shiftclass:转化的class,真实的类对象的地址
Magic:⽤于调试器判断当前对象是真的对象还是没有初始化的空间
Weakly_referenced:标志对象是否被指向或者曾经指向⼀个 ARC 的弱变量,
没有弱引⽤的对象可以更快释放。
Deallocating:标志对象是否正在释放内存
Has_sidetable_rc:当对象引⽤技术⼤于 10 时,则需要借⽤该变量存储进位
Extra_rc:当表示该对象的引⽤计数值,实际上是引⽤计数值减 1,
例如,如果对象的引⽤计数为 10,那么 extra_rc 为 9。如果引⽤计数⼤于 10,
则需要使⽤到下⾯的 has_sidetable_rc。
Magic,Has_sidetable_rc,Extra_rc不是很懂(以后再看看吧)
isa指向(简单介绍一下,下一个博客重点讲类)
对象(instance)的isa指向类对象,类(class)对象的isa 指向元类(metaClass)对象
对象的方法在类对象中,类方法在元类对象中
Class(类)的superClass指向父类,rootClass(NSObject的类)的superClass指向nil
metaClass(元类)的superClass指向父元类,metaClass(元类)的isa指向NSObject的元类
NSObject的元类的isa指向他自己