1216 字
6 分钟
iOS应用启动(二):环境配置与runtime初始化
本文基于
dyld-832.7.3与objc4-818.2源码
前言
结合上一文iOS应用启动(一):_dyld_start及符号断点我们找到了 _objc_init 的调用栈

- 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 调用
/************************************************************************ _objc_init* Bootstrap initialization. Registers our image notifier with dyld.* Called by libSystem BEFORE library initialization time**********************************************************************/1.1 源码分析
void _objc_init(void){ static bool initialized = false; if (initialized) return; initialized = true;
// fixme defer initialization until an objc-using image is found? /// 初始化环境变量 environ_init();
/// 关于线程`key`的绑定:比如线程数据的析构函数 tls_init();
/// 调用全局静态C++构造函数 static_init();
/// runtime 运行时环境初始化 runtime_init();
/// 初始化 `libobjc` 的异常处理系统 exception_init();
#if __OBJC2__ /// 缓存条件初始化 cache_t::init();#endif /// 启动回调机制 _imp_implementationWithBlock_init();
_dyld_objc_notify_register(&map_images, load_images, unmap_image);
#if __OBJC2__ didCallDyldNotifyRegister = true;#endif}下面我们将对一些重要步骤进行说明:
- 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[58337]: LOAD: category 'NSObject(NSObject)' scheduled for +loadobjc[58337]: LOAD: +[NSObject(NSObject) load]
objc[58337]: LOAD: category 'NSObject(NSObject)' scheduled for +loadobjc[58337]: LOAD: +[NSObject(NSObject) load]
objc[58337]: LOAD: class 'NSColor' scheduled for +loadobjc[58337]: LOAD: class 'NSApplication' scheduled for +loadobjc[58337]: LOAD: class 'NSBinder' scheduled for +loadobjc[58337]: LOAD: class 'NSColorSpaceColor' scheduled for +loadobjc[58337]: LOAD: class 'NSNextStepFrame' scheduled for +loadobjc[58337]: LOAD: +[NSColor load]
objc[58337]: LOAD: +[NSApplication load]
objc[58337]: LOAD: +[NSBinder load]
objc[58337]: LOAD: +[NSColorSpaceColor load]
objc[58337]: LOAD: +[NSNextStepFrame load]
objc[58337]: LOAD: category 'NSError(FPAdditions)' scheduled for +loadobjc[58337]: LOAD: +[NSError(FPAdditions) load]
objc[58337]: LOAD: class '_DKEventQuery' scheduled for +loadobjc[58337]: LOAD: +[_DKEventQuery load]
objc[58337]: LOAD: class 'RYModel' scheduled for +loadobjc[58337]: LOAD: +[RYModel load]自定义模型实现了+load方法被打印了出来,+[RYModel load]。
获取可用环境变量
在任意终端输入指令 export OBJC_HELP=1 即可获取环境变量列表,选择自己需要的进行使用,可以提高开发调试的效率哦。
➜ RyukieDevGitBook git:(master) ✗ export OBJC_HELP=1objc[57485]: Objective-C runtime debugging. Set variable=YES to enable.objc[57485]: OBJC_HELP: describe available environment variablesobjc[57485]: OBJC_PRINT_OPTIONS: list which options are setobjc[57485]: OBJC_PRINT_IMAGES: log image and library names as they are loadedobjc[57485]: OBJC_PRINT_IMAGE_TIMES: measure duration of image loading stepsobjc[57485]: OBJC_PRINT_LOAD_METHODS: log calls to class and category +load methodsobjc[57485]: OBJC_PRINT_INITIALIZE_METHODS: log calls to class +initialize methods...三、 static_init
调用全局静态C++构造函数
- 执行全局静态
C++构造函数 libc在dyld调用构造函数之前 调用_objc_init()- 所以我们要自己处理
/************************************************************************ static_init* Run C++ static constructor functions.* libc calls _objc_init() before dyld would call our static constructors,* so we have to do it ourselves.**********************************************************************/
static void static_init(){ size_t count; auto inits = getLibobjcInitializers(&_mh_dylib_header, &count); for (size_t i = 0; i < count; i++) { inits[i](); } auto offsets = getLibobjcInitializerOffsets(&_mh_dylib_header, &count); for (size_t i = 0; i < count; i++) { UnsignedInitializer init(offsets[i]); init(); }}3.1 验证
在我的代码中添加一个C++构造函数
__attribute__((constructor)) void ryFunc() { printf("我的constructor:%s \n", __func__);}- 发现并没有在这一步骤中执行我的C++构造函数
3.2 思考
这里其实调用的并非所有C++构造函数,而是在底层objc库中的构造函数。
在objc源码中添加一个构造函数:
__attribute__((constructor)) void ryFuncInObjc() { printf("我的constructor:%s \n", __func__);}
此处正常没有输出日志

日志输出:
我的constructor:ryFuncInObjc4.3 总结
- 这里调用的C++构造函数特指
objc源码中定义的一系列构造函数 - 因为全局构造函数非常重要,为了保证全局构造函数调用的及时性,所以这里自己进行了调用。
四、 runtime_init
runtime 运行时环境初始化
void runtime_init(void){ objc::unattachedCategories.init(32); objc::allocatedClasses.init();}通过查看这里的两个 init 方法我们发现,这两个是集合类型
public: template <typename... Ts> void init(Ts &&... Args) { new (_storage) Type(std::forward<Ts>(Args)...); }
Type &get() { return *reinterpret_cast<Type *>(_storage); }};4.1 unattachedCategories
static UnattachedCategories unattachedCategories;
} // namespace objc4.2 allocatedClasses
一个存储所有已经被 allocated 的类和元类的表
/************************************************************************ allocatedClasses* A table of all classes (and metaclasses) which have been allocated* with objc_allocateClassPair.**********************************************************************/namespace objc {static ExplicitInitDenseSet<Class> allocatedClasses;}五、 exception_init
初始化 libobjc 的异常处理系统
/************************************************************************ exception_init* Initialize libobjc's exception handling system.* Called by map_images().**********************************************************************/void exception_init(void){ old_terminate = std::set_terminate(&_objc_terminate);}- 需要的话也可以自定义一异常捕捉回调
FOUNDATION_EXPORT void NSSetUncaughtExceptionHandler(NSUncaughtExceptionHandler * _Nullable);六、 _imp_implementationWithBlock_init
启动回调机制。通常不会做什么,因为所有的初始化都是懒加载的,但是对于某些进程,会迫不及待的加载
trampolines dylib。
七、 _dyld_objc_notify_register
这里进行了镜像文件的读取与加载,后面会单独进行分析