15.iOS应用启动(二):环境配置与runtime初始化

本文基于 dyld-832.7.3objc4-818.2 源码

前言

结合上一文iOS应用启动(一):_dyld_start及符号断点我们找到了 _objc_init 的调用栈

dyldinit01
  • dyld`ImageLoaderMachO::doModInitFunctions

  • libSystem.B.dylib`libSystem_initializer

  • libdispatch.dylib`libdispatch_init

  • libdispatch.dylib`_os_object_init

  • libobjc.A.dylib`_objc_init

一、 _objc_init 做了什么

  • 引导初始化,使用 dyld 注册 images。

  • 在库初始化时间之前由 libSystem 调用

1.1 源码分析

下面我们将对一些重要步骤进行说明:

  • environ_init

    • 初始化环境变量

  • tls_init

    • 关于线程key的绑定:比如线程数据的析构函数

  • static_init

    • 调用全局静态C++构造函数

  • runtime_init

    • runtime 运行时环境初始化

  • exception_init

    • 初始化 libobjc 的异常处理系统

  • cache_t::init()

    • 缓存条件初始化

  • _imp_implementationWithBlock_init

    • 启动回调机制

  • _dyld_objc_notify_register

二、 environ_init

初始化环境变量

  • 修改环境变量能够有效帮助我们进行开发调试

修改环境变量进行调试

如这样一个场景,项目代码较多,引用的SDK也很多,想要知道哪些类实现了 +load 方法该怎么办?

  • 修改一个环境变量即可打印出来

    • 打开 Edit Scheme - Run - Argument 添加环境变量 OBJC_PRINT_LOAD_METHODS = YES 即可打印所有 +load 方法

OBJC_PRINT_LOAD_METHODS

效果:

自定义模型实现了+load方法被打印了出来,+[RYModel load]

获取可用环境变量

在任意终端输入指令 export OBJC_HELP=1 即可获取环境变量列表,选择自己需要的进行使用,可以提高开发调试的效率哦。

三、 static_init

调用全局静态C++构造函数

  • 执行全局静态 C++ 构造函数

  • libc dyld 调用构造函数之前 调用 _objc_init()

  • 所以我们要自己处理

3.1 验证

在我的代码中添加一个C++构造函数

  • 发现并没有在这一步骤中执行我的C++构造函数

3.2 思考

这里其实调用的并非所有C++构造函数,而是在底层objc库中的构造函数。

在objc源码中添加一个构造函数:

static_init

此处正常没有输出日志

static_init

日志输出:

4.3 总结

  • 这里调用的C++构造函数特指 objc源码中定义的一系列构造函数

  • 因为全局构造函数非常重要,为了保证全局构造函数调用的及时性,所以这里自己进行了调用。

四、 runtime_init

runtime 运行时环境初始化

通过查看这里的两个 init 方法我们发现,这两个是集合类型

4.1 unattachedCategories

4.2 allocatedClasses

一个存储所有已经被 allocated 的类和元类的

五、 exception_init

初始化 libobjc 的异常处理系统

  • 需要的话也可以自定义一异常捕捉回调

六、 _imp_implementationWithBlock_init

启动回调机制。通常不会做什么,因为所有的初始化都是懒加载的,但是对于某些进程,会迫不及待的加载 trampolines dylib

七、 _dyld_objc_notify_register

这里进行了镜像文件的读取与加载,后面会单独进行分析

Last updated

Was this helpful?