要分析类的启动加载流程,我们可以从一个点入手,即load方法的调用,因为该方法会在应用启动时自动调用,我们根据这个特性查看一下调用堆栈,如下:
|
|
可以看到,程序是从_dyld_start这个函数开始运行的,老办法,我们到dyld的源码中去一探究竟。
|
|
dyld的入口函数也是使用汇编来编写的,我们不需要逐行理解,找一下关键字,结合上面的堆栈信息和汇编注释,我们定位到dyldbootstrap::start这个关键方法,最终跟踪到以下调用链:
|
|
上面的流程有一个比较奇怪的调用出现在objc::load_images,因为全局都没有找到这个方法的任何申明和定义,我们猜测这个函数可能是外界传过来的一个函数指针,那么如何验证呢?回到线索中断前的最后一个方法中去:
|
|
果然,在里面我们发现有一些比较敏感的关键字sNotifyObjCInit,这个应该跟objc的初始化有关系,我们全局搜索一下,最后定位到下面的函数:
|
|
上面的注释写得已经比较清楚,这个函数专供 objc runtime。那我们就去runtime的源码中找找嘛。
|
|
果然在_objc_init初始化函数中发现了调用逻辑,分别注册了map_images和load_images两个回调函数,看到这里熟悉应用启动流程的同学应该想到了,这不就是可执行文件的mmap和load么。但是新的问题又来了,这个_objc_init又是在什么时候被调用的呢?我们直接在里面搞个断点看看调用堆栈:
|
|
前面我们已经看过的函数跳过,直接从ImageLoaderMachO::doInitialization开始。
|
|
看注释和异常文案,libSystem必须最先被初始化,这也解释了上面的堆栈情况。行吧,那我们再去libSystem的源码中看看有没有libSystem_initializer这个初始化方法。
果然是有的,去掉一些不关系的信息,大概如下:
|
|
可以看到,里面做了很多系统库的初始化工作,我们也看到了熟悉的身影
|
|
看了一堆初始化方法,还是没有找到我们要的_objc_init呀,这货到底藏哪了。我们回到堆栈,发现在libdispatch的初始化后又调用了_os_object_init这个方法,猜测是不是跟这个方法有关系呢?没办法,我们还得去libdispatch的源码中看一下。
|
|
呵呵,妖怪哪里跑!!!
小结
现在我们终于把OC类从app启动到加载的整个流程跑通了,但是光知道流程显然还是不够啊,类是如何从二进制文件被创建成类对象的呢?我们之后再继续探索这一块的内容吧。