本文由快学吧个人写作,以任何形式转载请表明原文出处
一、资料准备
对应mac的版本是11.1。可根据自己的系统版本挑选可以进行调试的源码。
因为mac的版本我没有更新过,一直停留在11.1,所以我选择的dyld源码也是老版本的dyld3,最新版本的dyld4会在系统更新之后再单独写一篇关于dyld4的个人见解。
二、思路
- 从上一章可知,app加载的时候,动态库都会变成了镜像文件。那么app要使用它,就需要把镜像文件的内容映射出来到内存上,才可以被app使用。
- iOS中映射镜像文件的事情就是dyld来做,库本身是不能自己从镜像中映射自己的数据出来的。
- 从上一章可知,对于我们常用的objc库,是dyld让它执行了初始化程序。
- 在dyld对objc进行库的初始化的时候,objc的初始化函数
_objc_init()
会注册一个回调_dyld_objc_notify_register
。既然objc自己不能获取镜像数据,那就通过回调,借用dyld可以操作镜像,来对自己镜像文件中的数据进行处理。 - 那么
objc_init()
的初始化到底都做了什么?
三、objc的初始化流程简析
1. 进入objc源码,搜索_objc_init(void)
源码 :
这里我分了三个区域,本章只看2.初始化区。1区不做简析。3区和上一章关联。map_images
和load_images
后面学习做简析。
2. environ_init
系统级别的环境变量的初始化。
- 环境变量最开始默认都是NO
- xcode可以设置环境变量的值,将默认的NO改成YES。更改方式如下 :
- 如何找到环境变量都有哪些?最简单的方法是在终端也就是terminal中输入:
export OBJC_HELP=1
。如果没有效果,接着调用open OBJC_HELP=1
-
也可以在818.2的源码中的
Project Headers
文件夹下,找到objc_env.h
,里面也有。 -
举例,以
OBJC_DISABLE_NONPOINTER_ISA
来举例。这个环境变量如果设置成YES,则isa就会变成纯净的isa,也就是isa只有一个Class成员,没有bitFields了。
(1). 不设置的时候 :
(2). 设置OBJC_DISABLE_NONPOINTER_ISA
= YES
(3). 再看类的地址和实例的isa地址,变成一样的了。也就是把isa其他的优化都不要了,只保留了Class的地址。
3. tls_init
线程局部存储的初始化。tls是Thread Local Storage的缩写。是将一个key和一个析构函数的函数地址绑定在一起。
4. static_init
静态函数的初始化。仅是系统级别的静态函数在这里做初始化。
我们自己写的静态函数则是被dyld映射数据并加载到内存之后才会调起初始化。
5. runtime_init
runtime的初始化。创建了两个容器,一个是分类容器,一个是类容器。一般情况下,容器大多是表的形式。
6. exception_init
异常处理的初始化。
7. cache_t::init();
缓存的初始化。有关缓存,有简单的分析过cache_t。源码贴在这里,没理解。
8. _imp_implementationWithBlock_init()
9. _dyld_objc_notify_register
objc库和dyld的联系方式,是通过回调通知来完成的。上一章已经找到了相关的dyld源码。
这其中有三个函数 :
map_images : 映射镜像
load_images : 加载镜像
unmap_images : 注销镜像或者说取消映射镜像。
后面的重点会在map_images和load_images的源码中