十八、app加载流程(二)_objc_init()

100 阅读3分钟

本文由快学吧个人写作,以任何形式转载请表明原文出处

一、资料准备

  1. objc4-818.2

对应mac的版本是11.1。可根据自己的系统版本挑选可以进行调试的源码。

  1. dyld3-852

因为mac的版本我没有更新过,一直停留在11.1,所以我选择的dyld源码也是老版本的dyld3,最新版本的dyld4会在系统更新之后再单独写一篇关于dyld4的个人见解。

二、思路

  1. 上一章可知,app加载的时候,动态库都会变成了镜像文件。那么app要使用它,就需要把镜像文件的内容映射出来到内存上,才可以被app使用。
  2. iOS中映射镜像文件的事情就是dyld来做,库本身是不能自己从镜像中映射自己的数据出来的。
  3. 上一章可知,对于我们常用的objc库,是dyld让它执行了初始化程序。
  4. 在dyld对objc进行库的初始化的时候,objc的初始化函数_objc_init()会注册一个回调_dyld_objc_notify_register。既然objc自己不能获取镜像数据,那就通过回调,借用dyld可以操作镜像,来对自己镜像文件中的数据进行处理。
  5. 那么objc_init()的初始化到底都做了什么?

三、objc的初始化流程简析

1. 进入objc源码,搜索_objc_init(void)源码 :

这里我分了三个区域,本章只看2.初始化区。1区不做简析。3区和上一章关联。map_imagesload_images后面学习做简析。

图片.png

2. environ_init

系统级别的环境变量的初始化。

图片.png

  1. 环境变量最开始默认都是NO
  2. xcode可以设置环境变量的值,将默认的NO改成YES。更改方式如下 :

图片.png

图片.png

  1. 如何找到环境变量都有哪些?最简单的方法是在终端也就是terminal中输入:export OBJC_HELP=1。如果没有效果,接着调用open OBJC_HELP=1

图片.png

  1. 也可以在818.2的源码中的Project Headers文件夹下,找到objc_env.h,里面也有。

  2. 举例,以OBJC_DISABLE_NONPOINTER_ISA来举例。这个环境变量如果设置成YES,则isa就会变成纯净的isa,也就是isa只有一个Class成员,没有bitFields了。

(1). 不设置的时候 :

图片.png

(2). 设置OBJC_DISABLE_NONPOINTER_ISA = YES

图片.png

(3). 再看类的地址和实例的isa地址,变成一样的了。也就是把isa其他的优化都不要了,只保留了Class的地址。

图片.png

3. tls_init

线程局部存储的初始化。tls是Thread Local Storage的缩写。是将一个key和一个析构函数的函数地址绑定在一起。

图片.png

4. static_init

静态函数的初始化。仅是系统级别的静态函数在这里做初始化。

我们自己写的静态函数则是被dyld映射数据并加载到内存之后才会调起初始化。

图片.png

5. runtime_init

runtime的初始化。创建了两个容器,一个是分类容器,一个是类容器。一般情况下,容器大多是表的形式。

图片.png

6. exception_init

异常处理的初始化。

图片.png

7. cache_t::init();

缓存的初始化。有关缓存,有简单的分析过cache_t。源码贴在这里,没理解。

图片.png

8. _imp_implementationWithBlock_init()

图片.png

9. _dyld_objc_notify_register

objc库和dyld的联系方式,是通过回调通知来完成的。上一章已经找到了相关的dyld源码。

这其中有三个函数 :

map_images : 映射镜像

load_images : 加载镜像

unmap_images : 注销镜像或者说取消映射镜像。

后面的重点会在map_images和load_images的源码中