一、 应用程序加载
- 系统调用exec()会让我们的应用程序映射到新的地址空间
- 然后通过dyld进行加载、链接、初始化主程序和应用程序所依赖的各种动态库
- 最后在initializeMainExecutable犯法中经过一系列的初始化调用notifySingle函数,该函数会执行一个load_images的回调
- 在doModInitFunctions函数内部调用attribute((contrustor))的C++函数
- 在dyld返回主程序的入口函数,开始进入主程序的main函数
在main函数执行过程中,其实dyld还会在流程中初始化libSystem,而libSystem又会去初始化libDispatch,在libDispatch初始化方法里边又会调用os_object_int, 在os_object_int内部就会调用objc_init,从而才会进入我呢相隔类的加载过程。这就是整个应用程序加载的大致过程
二、类的加载
应用程序相关的加载和编译在main函数之前,在read_image字段中进行镜像images读取
在read_images中找到关于类的信息
- 关于非懒加载类
Realize non-lazy classes (for +load methods and static instances)
在自定义的类方法中,我们探究所定义的类加载到内存中,根据代码的执行,我们找到如下的方法:
static Class realizeClassWithoutSwift(Class cls, Class previously)
在中途通过mangleName筛选到我们当前的类
- 通过macho文件里的data返回一个类的class_ro_t从而赋值到响应的ro中即cleanMemory
- 判断是否为元类,通过第一步的ro数据,读取到rw
- 如果非元类,即进行相应的数据从ro到rw中,通过re->set_ro(ro);操作完成
- 完成相关类的继承链,从而为以后的方法查找以及后续的初始化埋下伏笔
此时的类还只是带地址的一个名字,并没有实现,但我们相关的ro和rw是有值的
通过以上方法我们知道了相关的类的信息,并且成功将数据映射到内存,包括ro和rw的赋值都在此时实现,所以在此去实现相关元类meta信息,从而进入到最后的methodizeClass(cls, previously);中
最后进入到attachToClass中,从而进行相关类的方法、协议的实现
- 懒加载类
在+load方法没有实现的时候,我们发现程序的执行和之前的流程不太一样,程序会先进入到底层的objc_msgSent,因为我们在创建
Person *per = [Person alloc];
程序底层会进行一次消息转发,从而进入到消息的查找从而进入到lookUpImpOrForward
进行程序实现的判断
之后进入到realizeClassMaybeSwiftAndLeaveLocked中,从而实现和非懒加载类一样执行realizeClassWithoutSwift和methodizeClass的流程,从而实现该类的信息
三、分类的加载
- 3.1分类的概念和结构
分类的内部结构 - name:分类的名字
- cls 分类的类
- instance_methods: 实例方法
- class_methods:类方法
- protocols:协议列表
- properties:属性列表
- 3.2、分类加载到应用程序时机
类加载执行methodizeClass(cls, previously);
程序加载分裂的机制和时机
methodizeClass(cls, previously)
通过定位到我们的类,然就进行打印相关的列表
通过list,假如为无序时,我们进行相关排序操作
method_list_t *list = ro->baseMethods();
if (list) {
prepareMethodLists(cls, &list, 1, YES, isBundleClass(cls));
if (rwe) rwe->methods.attachLists(&list, 1);
}
通过这个方法去加载相关分类的属性,方法,协议等,实现分类,从而对内存中的rwe进行调用和开辟
通过以上的attachList方法,从而循环对分类的属性、协议、方法等操作具体实现
- 判断要添加的数量是否为0,如果为0,则直接返回
- 对ATTACH_BUFSIZ - ++mcount所有的方法进行倒序插入
- 判断attachLists的list_array_tt二维数组有多一个一位数组
如果是,说明多对多的关系
通过realloc对容器进行重新分配,大小是原来的大小加上新增的大小
通过memmove把原来的数据移动到尾部
最后把新的数据拷贝到容器的起始位置, - 如果调用的attachLists的list_array_tt二位数组为空且新增控件大小数目为1,则直接取attachList的第一个list
- 如果当前调用的attachLists的list_array_tt二位数组只有一个一位数组
如果是,说明是一对多的关系
通过realloc对容器进行重新分配,大小是原来的大小加上新增的大小
因为原来只有一个一位数组,所以直接赋值到新array的最后一个位置
最后把新的数据拷贝到容器的起始位置