我们在之前的探究过程中发现dyld加载中会调用到_objc_init,这篇文章我们从_objc_init开始研究其具体做了什么。
_objc_init源码
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();//环境变量
tls_init();//线程绑定key
static_init();//c++静态构造函数运行
runtime_init();//运行时初始化
exception_init();//libobjc异常处理
cache_init();//缓存初始化
_imp_implementationWithBlock_init();
// map_images() load_images()
_dyld_objc_notify_register(&map_images, load_images, unmap_image);
#if __OBJC2__
didCallDyldNotifyRegister = true;
#endif
}
从_objc_init的源码分析,主要分成以下部分:
- environ_init:环境变量初始化
- tls_init:关于线程key的绑定
- static_init:运行C++静态构造函数(只会运行系统级别的构造函数),在dyld调用静态析构函数之前,libc会调用_objc_init
- runtime_init:runtime运行时环境初始化,里面操作是unattachedCategories、allocatedClasses(表的初始化)
- exception_init:libobjc异常处理初始化
- cache_init: 缓存初始化
- _imp_implementationWithBlock_init :启动回调机制。
- _dyld_objc_notify_register: dyld的注册
在这些部分中,包含了各种初始化的操作,以及最重要的部分_dyld_objc_notify_register,其中有使用到map_images(处理dyld给的镜像文件)、load_images(加载映射镜像文件)。
_dyld_objc_notify_register源码
void _dyld_objc_notify_register(_dyld_objc_notify_mapped mapped,
_dyld_objc_notify_init init,
_dyld_objc_notify_unmapped unmapped);
map_images对应mapped,load_images对应init,unmap_image对应unmapped。
从_dyld_objc_notify_register方法定义位于dyld_priv.h文件中,其对应的实现是在dyld的源码中的,在dyld的源码中我们找到了对应的处理
void _dyld_objc_notify_register(_dyld_objc_notify_mapped mapped,
_dyld_objc_notify_init init,
_dyld_objc_notify_unmapped unmapped)
{
log_apis("_dyld_objc_notify_register(%p, %p, %p)\n", mapped, init, unmapped);
gAllImages.setObjCNotifiers(mapped, init, unmapped);
}
void AllImages::setObjCNotifiers(_dyld_objc_notify_mapped map, _dyld_objc_notify_init init, _dyld_objc_notify_unmapped unmap)
{
_objcNotifyMapped = map;
_objcNotifyInit = init;
_objcNotifyUnmapped = unmap;
.....
}
从这里我们可以看出,map_images、load_images、unmap_image的调用实现是来自于dyld的。程序运行先是dyld_start链接操作,链接主程序、执行主程序初始化,所有库初始化,然后执行objc_init函数,写入注册函数,通知dyld可以继续执行其他流程。
map_images
void
map_images(unsigned count, const char * const paths[],
const struct mach_header * const mhdrs[])
{
mutex_locker_t lock(runtimeLock);
return map_images_nolock(count, paths, mhdrs);
}
map_images会调用map_images_nolock,map_images_nolock中代码过长我们就不一一贴出来分析了,直接看其最关键的代码
if (hCount > 0) {
_read_images(hList, hCount, totalClasses, unoptimizedTotalClasses);
}
_read_images从源码分析其实现
- 条件控制加载一次。
- 修复selector涉及的混乱问题。
- 发现类,修复未解决类,标记捆绑类。
- 修复重新映射类
- 修复旧的objc_msgSend_fixup调用
- 发现protocols,修复protocol引用
- 修复@protocol引用
- 分类加载处理
- 类加载处理
- 未解析处理的类,防止被侵犯。
在这过程中,有些比较重要的处理我们需要注意
static size_t UnfixedSelectors;
{
mutex_locker_t lock(selLock);
for (EACH_HEADER) {
if (hi->hasPreoptimizedSelectors()) continue;
bool isBundle = hi->isBundle();
SEL *sels = _getObjc2SelectorRefs(hi, &count);
UnfixedSelectors += count;
for (i = 0; i < count; i++) {
const char *name = sel_cname(sels[i]);
SEL sel = sel_registerNameNoLock(name, isBundle);
if (sels[i] != sel) {
sels[i] = sel;
}
}
}
}
这段源码中,sels[i] != sel进行带地址的字符串匹配,两者字符会一样但是地址可能不一样。
(lldb) po sels[i]
"class"
(lldb) po sel
"class"
(lldb) p/x sels[i]
(SEL) $3 = 0x000000010045ec5e "class"
(lldb) p/x sel
(SEL) $4 = 0x00007fff77889d1d "class"
(lldb)
for (i = 0; i < count; i++) {
Class cls = (Class)classlist[i];
Class newCls = readClass(cls, headerIsBundle, headerIsPreoptimized);
if (newCls != cls && newCls) {
// Class was moved but not deleted. Currently this occurs
// only when the new class resolved a future class.
// Non-lazily realize the class below.
resolvedFutureClasses = (Class *)
realloc(resolvedFutureClasses,
(resolvedFutureClassCount+1) * sizeof(Class));
resolvedFutureClasses[resolvedFutureClassCount++] = newCls;
}
}
这段代码中我们主要是看readClass,这个方法会读编译器编写的类和元类。cls在这里会由一个地址,经过readClass读取类信息,readClass读取的流程后续再进行分析。
接下来看
for (EACH_HEADER) {
classref_t const *classlist =
_getObjc2NonlazyClassList(hi, &count);
for (i = 0; i < count; i++) {
Class cls = remapClass(classlist[i]);
if (!cls) continue;
addClassTableEntry(cls);
if (cls->isSwiftStable()) {
if (cls->swiftMetadataInitializer()) {
_objc_fatal("Swift class %s with a metadata initializer "
"is not allowed to be non-lazy",
cls->nameForLogging());
}
// fixme also disallow relocatable classes
// We can't disallow all Swift classes because of
// classes like Swift.__EmptyArrayStorage
}
realizeClassWithoutSwift(cls, nil);
}
}
这里是分类的加载处理,具体的加载情况及加载时机我们后续再分析。