Objective-C中在通过定义一个+load()方法,让+load()方法在所有OC对象创建前被执行,同时也会在main函数执行前执行。一般情况下我们使用+load()方法进行一些hook、实现一些全局初始化的逻辑。
除了建立C++全局对象、实现OC类的+load方法来进行一些全局的初始化逻辑外,我们还可以定义带有特殊标志的C函数来实现main函数执行前以及main函数执行完毕后的处理逻辑
代码实例 输出结果//main函数执行前被执行的函数
__attribute__((constructor))
//main函数执行完毕后被执行的函数
__attribute__((destructor))
通过输出结果可以确定:
1、带有特殊标识__attribute__((constructor))的方法会在main方法执行前执行;
2、带有特殊标识__attribute__((destructor))的方法会在main方法执行后执行;
main函数执行前发生了些什么呢?
操作系统在启动一个程序时,内核会为程序创建一个进程空间,并且会为进程创建一个主线程,主线程会执行各种初始化操作,完成后才开始执行我们在程序中定义的main函数
也就是说main不是第一个执行的函数,在main之前还会执行一系列的方法
_objc_init符号断点通过设置_objc_init符号断点,然后结合bt命令查看在_objc_init之前发生了哪些事情(启动项目,断点到来之后,在控制台输入bt,会出现如下结果)
bt输出内容看到栈底的dyldbootstrap::start()方法,继而调用了dyld::_main()方法,其中完成了递归加载动态库过程,由于libSystem默认引入,栈中出现了libSystem_initializer的初始化方法。
可以看到_objc_init调用顺序,先libSystem_initializer调用libdispatch_init,再到_objc_init初始化runtime.
runtime初始化后不会闲着,在_objc_init中注册了几个对象,从dyld这里接手几个活,其中包括初始化相应依赖库里的类结构,调用依赖库里所有load方法。
initializer方法是最后调用的,当initializer方法被调用前dyld会通知runtime进行类结构初始化,然后再通知调用+load方法,这些目前都发生在main函数前,但是由于lazy bind机制,依赖库多数都是在使用时才进行bind,所以这些依赖库的类结构初始化都是发生在程序里第一次使用到该依赖库时才进行
dyld
系统先读取App的可执行文件(Mach-O文件),从里面获得dyld的路径,然后加载dyld,dyld去初始化运行环境
1、dyld将我们可执行文件以及插入的lib加载进内存,生成对应的image
2、对上面生成的image进行链接。其主要有对image进行load(加载)、rebase(基地址复位),bind(外部符号绑定)
3、这一步主要是调用所有image的initalizer方法进行初始化。这里的initalizers方法并非名为Initalizers的方法,而是C++静态对象初始化构造器,atribute(constructor)进行修饰的方法,在ImageLoader类中initializer函数指针锁指向该初始化方法的地址
可以在程序中设置环境变量DYLD_PRINT_INITIALIZERS为1来打印出程序的各种依赖库的initializer方法
ImageLoader
这个image不是图片的意思,它大概表示一个二进制文件(可执行文件或so文件),里面是被编译过的符号、代码等,所以imageLoader作用是将这些文件加载进内存,且每一个文件对应一个imageLoader实例来负责加载
1.在程序运行时它先将动态链接的image递归加载(也就是上面ImageLoader的递归调用)
2.再从可执行文件image递归加载所有符号
runtime与load
libSystem是若干个系统lib的集合,所以它只是一个容器lib而已,而且它也是开源的,里面实质上就是一个文件: init.c 由libSystem_initializer逐步调用到了_objc_init,这里就是objc和runtime的初始化入口。
除了runtime环境的初始化外,_objc_init中绑定了新image被加载后的callback
load方法加断点后的调用堆栈通过调用堆栈可以清晰的知道main之前整体的调用次序:
1.dyld开始将程序二进制文件初始化
2.交由imageLoader读取image,其中包含了我们的类,方法等各种符号
3.由于runtime向dyld绑定了回调,当image加载到内存后,dyld会通知runtime进行处理
4.runtime接手后调用map_images做解析和处理,接下来load_images中调用call_load_methods方法,遍历所有加载进来的Class,按继承层级依次调用Class的+load方法和Category的+load方法。
至此,可执行文件中和动态库所有的符号(Class, Protocol,Selector,IMP,...)都已经按格式成功加载到内存中,被runtime所管理,再这之后,runtime的那些方法(动态添加Class,swizzie等才能生效)
网友评论