iOS 底层探索篇 ——类的加载原理(中)
_read_images流程分析ro,rw
上回说到readClass
里面给类加上了名字,那么ro
,rw
是在哪里加的呢?回到_read_images
打上断点一点一点探究。
在remap classes
这里打上断点后运行,发现没有进去。
懒加载和非懒加载
为了节约内存和速度,苹果不会把所有的类都加载,而是把我们自己的类以一种懒加载的形式按需加载,当实现load之后,就是非懒加载的形式了。而如果没有实现load,就是懒加载类,当类第一次收到消息的时候,就会被加载。
懒加载类情况:数据加载推迟到第一次消息的时候
- lookUpImpOrForward
- realizeClassMaybeSwiftMaybeRelock
- realizeClassWithoutSwift
- methodizeClass
非懒加载类情况:map_images的时候 加载所有类数据
- _getObjc2NonlazyClassList
- readClass
- realizeClassWithoutSwift
- methodizeClass
往下走,看到objc_msgSend_fixup
,和类无关就跳过不看。继续往下走discover protocols
也和类无关不看。在往下看到protocol references
,也和类无关,继续往下走。discover categories
,分类,继续跳过。继续往下走看到realize non-lazy classes
,这个是非懒加载类的实现,和类的实现有关,接下来就研究这里。
因为要研究自己的类,所以需要把实现类的load方法
,否则LGPerson就无法进入这里。
添加load方法后运行,发现是从read_images
的时候进来的。
注释掉load 方法后运行,发现在realizeClassWithoutSwift有实现LGPerson,打印一下发现确实是在收到消息的时候调用的。
之后就开始研究了。加入方法确认是我们要研究的类后,运行程序。
运行后发现的确进来了。
往下走,发现调用了realizeClassWithoutSwift
。
进去后输出一下cls,发现是LGPerson
继续往下走
然后输出po,看到这里打出ro的baseMethodList
,这说明了方法等数据都已经加载进来。
往下走去找哪里进行了赋值。发现进入了这里,而ro,rw也是在这里设置。而看到rw->set_ro(ro)
,这也是为什么rw的数据和ro是相似的,除非是动态处理的地方,比如addMethod
,addProtocols
。
往下走看到这里如果是元类的话,就会进行rawIsa
的处理,所以这也是为什么元类的isa是纯isa
的原因。
往下看,看到这里如果设置了DisableNonpointerIsa
为true
,就会设instancesRequireRawIsa
为true
,然后调用setInstancesRequireRawIsaRecursively
。这也是为什么设置环境变量DisableNonpointerIsa为YES的时候,会得到纯isa的原因。
继续往下走,看到这里设置了父类和isa
在往下看到在这里设置了子类。
在下面设置一个条件来确定是LGPerson类,并加上一个条件判断不是元类以免元类进来。
运行后发现ro
的baseMethodList
还是没有内容
接下来就进入methodizeClass
里面查看。
把之前的条件复制进来,打下断点后运行。
继续往下走,看到这里获取了ro
的baseMethods
往下走一行,等赋值后查看list,发现是有地址的,尝试打印,发现和之前的打印出来的没有什么区别。
继续往下走,去到prepareMethodLists
里面,在里面打下断点
继续运行后,发现进入到了fixupMethodList
里面。
进入后看到有setName
的操作,而name
的值则是sel
,sel_cname
是将sel
转为const char *
类型的值。
继续往下看,看到这个地址还会根据sel的地址来进行排序。不要尝试去改小地址的位置,因为小地址排完序后不可变。也不要去改变不是标准大小的大列表的顺序,因为stable_sort无法正确拷贝入口。
那么是否有排序了呢,在排序前后打印出sel来查看是否排序
运行时一定要确认是LGPerson类进来的,然后在研究。发现确实对方法进行了排序。
接着回到methodizeClass
,看看ro里的baseMethods是否有值。发现这里有值了,但是这里的顺序不一样了,可以注意到分类的saysomething和主类的saysomething放在一起了,而且分类的就在主类的前面,这也解释了为什么方法查找中为什么method–就是调用分类的同名方法。这里也说明了prepareMethodLists里面进行了方法的排序等工作。
之前看到methodizeClass的注释写着Attach categories
(链接分类),那么分类在这里会怎么处理呢?探索之前,先来了解什么是分类。
分类
要研究分类,就先clang一下看一下c啪啪文件。生成cpp文件后打开,在最后一行看到了_category_t
就有了我们的LG分类,
来查找一下_category_t是什么东西,发现也是一个结构体。这里为什么有instance_methods
和class_methods
,而不直接写一个methodLIst之类的东西呢?这是因为分类没有元类
,所以方法都写在这里面,所以需要分开写。
往下看,看到这里写了个LGPerson。在静态编译的时候,还没跑起来运行时,名字不知道是什么,所以写了个LGPerson上去。而下面的0代表没有protocol。
添加一个协议然后重新clang一下再来看。发现下面就不为0了。
往下看,分类里的方法都看到了,但是确没有set和get方法。
在回到源码里查看category_t,看看是否和cpp里面的一样。发现多了个_classProperties
,但是注释写着他不是一直都有的。
那么分类是如何如何写到类中的呢?且听下回分解