底层原理:dyld和objc关联

我们在之前的探究过程中发现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);
        }
    }

这里是分类的加载处理,具体的加载情况及加载时机我们后续再分析。

你可能感兴趣的:(底层原理:dyld和objc关联)