07-OC底层原理之类的加载流程

简介: 07-OC底层原理之类的加载流程

dyld

dyld(the dynamic link editor)是苹果的动态链接器,是苹果操作系统一个重要组成部分,系统加载、链接mach-O文件,就是由dyld负责的,而且它是开源的,可以通过苹果官网下载它的源码来阅读理解它的工作原理,通过它可以了解系统加载动态库的更多细节;

dyld的加载流程从_dyld_start开始 -> 一系列流程 -> libSystem -> libdispatch -> objc中的_objc_init_objc_init又调用了dyld中实现的_dyld_objc_notify_register注册回调函数,调用dyld中的_dyld_objc_notify_register的时候,又调用了map_images函数、loadImages函数,于是就把objcdyld进行了关联;

_objc_init

首先看一下_objc_init入口函数:

void _objc_init(void)
{
    static bool initialized = false;
    if (initialized) return;
    initialized = true;
    // 读取环境变量
    environ_init();
    //线程的绑定
    tls_init();
    //调用c++静态构造函数
    static_init();
    //主要是对unattachedCategories和allocatedClasses的初始化;
    runtime_init();
    //异常处理
    exception_init();
    //缓存处理
    cache_t::init();
    //某些进程的特殊处理
    _imp_implementationWithBlock_init();
    // 注册回调函数
    _dyld_objc_notify_register(&map_images, load_images, unmap_image);
    //标记已经加载完成
    didCallDyldNotifyRegister = true;
}

map_images

 在_objc_init中通过_dyld_objc_notify_register函数,内部又调用了map_images、load_images和unmap_image;看一下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);
}

_read_images

 接下来看_read_images函数:
void _read_images(header_info **hList, uint32_t hCount, int totalClasses, int unoptimizedTotalClasses)
{
    //glt: 只会执行1次的代码加载
    if (!doneOnce) { ... }
    //glt: 修复预编译阶段的@selector不相同(SEL的名称或者地址不同)的问题
    //glt: 会对比Macho里面读取的sel和dyld加载的(sels[索引])sel对比,最终以Mach-o里面的为准
    { ... }
    ts.log("IMAGE TIMES: fix up selector references");
    //glt: 修复预编译阶段类的不同,对比Mach-o里面的类列表和readClass里面的类,最终以readClass为准
    for (EACH_HEADER) {
        for (i = 0; i < count; i++) {
            //cls这里只能获取到地址
            Class cls = (Class)classlist[i];
            //调用addNamedClass后,有了类名了
            Class newCls = readClass(cls, headerIsBundle, headerIsPreoptimized);
            ...
        }
    }
    ts.log("IMAGE TIMES: discover classes");
    //glt: 修复重映射一些没有被镜像文件加载进来的类
    if (!noClassesRemapped()) { ... }
    ts.log("IMAGE TIMES: remap classes");
    //glt: 修复旧objc_msgSend_fixup调用(imp重指向)
    for (EACH_HEADER) { ... }
    ts.log("IMAGE TIMES: fix up objc_msgSend_fixup");
    //glt: 查询并修复协议
    // Discover protocols. Fix up protocol refs.
    for (EACH_HEADER) { ... }
    ts.log("IMAGE TIMES: discover protocols");
    //glt: @protocol引用修复,修复没有被加载的协议
    for (EACH_HEADER) { ... }
    ts.log("IMAGE TIMES: fix up @protocol references");
    //glt: 仅在完成初始化分类后才执行此操作。对于启动时出现的分类,被推迟到_dyld_objc_notify_register 调用完成后的第一个load_images调用,即loadAllCategories();
    if (didInitialAttachCategories) { ... }
    ts.log("IMAGE TIMES: discover categories");
    // 处理非懒加载类 (实现了load方法)
    for (EACH_HEADER) {
        //从Mach-O中读出所有的类列表nlclslist -> _getObjc2NonlazyClassList -> __objc_nlclslist
        classref_t const *classlist = hi->nlclslist(&count);
        for (i = 0; i < count; i++) {
            Class cls = remapClass(classlist[i]);
            if (!cls) continue;
            //遍历classlist将class重新映射,得到的新class和metaClass插入类表中
            addClassTableEntry(cls);
            realizeClassWithoutSwift(cls, nil);
        }
    }
    ts.log("IMAGE TIMES: realize non-lazy classes");
    // 优化没有被处理的类
    if (resolvedFutureClasses) { ... }
     ts.log("IMAGE TIMES: realize future classes");
    if (DebugNonFragileIvars) {
        realizeAllClasses();
    }
}

map_images_nolock

map_images内部又调用了map_images_nolock:
void
map_images_nolock(unsigned mhCount, const char * const mhPaths[],
                  const struct mach_header * const mhdrs[])
{
    // 执行首次初始化,需再普通库初始化前调用
    if (firstTime) {
        preopt_init();
    }
    // Find all images with Objective-C metadata.
    hCount = 0;
    // Count classes. Size various table based on the total.
    int totalClasses = 0;
    int unoptimizedTotalClasses = 0;
    //块处理,给hCount赋值
    { ... }
    //执行一次性的运行时初始化,该初始化必须延迟到找到可执行文件本身,如果可执行文件不包含Objective-C代码,但后来动态加载了Objective-C,在infoList中可能不存在可执行文件;
    if (firstTime) { ... }
    if (hCount > 0) {
        //读取镜像文件
        _read_images(hList, hCount, totalClasses, unoptimizedTotalClasses);
    }
    firstTime = NO;
    //执行完之后调用镜像文件load函数
    for (auto func : loadImageFuncs) { ... }
}

主要做了以下几件事:

  • 只会执行1次的代码加载
  • 修复预编译阶段的@selector不相同(SEL的名称或者地址不同)的问题
  • 修复预编译阶段的@selector不相同(SEL的名称或者地址不同)的问题,会对比Macho里面读取的sel和dyld加载的(sels[索引])sel对比,最终以Mach-o里面的为准
  • 修复预编译阶段类的不同,内部调用了readClass,对比Mach-o里面的类列表和readClass里面的类,最终以readClass为准;
  • 修复重映射一些没有被镜像文件加载进来的类
  • 修复旧objc_msgSend_fixup调用(imp重指向)
  • 查询并修复协议
  • @protocol引用修复,修复没有被加载的协议
  • 仅在完成初始化分类后才执行此操作,对于启动时出现的分类,被推迟到_dyld_objc_notify_register 调用完成后的第一个load_images调用,即loadAllCategories()
  • 处理非懒加载类 (实现了load方法),内部会调用realizeClassWithoutSwift
  • 优化没有被处理的类

readClass

接下来调用readClass,读取类信息:

Class readClass(Class cls, bool headerIsBundle, bool headerIsPreoptimized)
{
    //获取类名
    const char *mangledName = cls->nonlazyMangledName();
    if (missingWeakSuperclass(cls)) { ... }
    cls->fixupBackwardDeployingStableSwift();
    Class replacing = nil;
    //移除现在不实现,将来才实现的类,注意这里面对rw的赋值并不是真正的为每个类的cls赋值
    if (mangledName != nullptr) {
        if (Class newCls = popFutureNamedClass(mangledName)) {
            ...
        }
    }
    if (headerIsPreoptimized && !replacing) {
        ASSERT(mangledName == nullptr || getClassExceptSomeSwift(mangledName));
    } else {
        if (mangledName) {//将类名绑定到数据表中
            addNamedClass(cls, mangledName, replacing);
        } else {
            Class meta = cls->ISA();
            const class_ro_t *metaRO = meta->bits.safe_ro();
        }
        //加入到需要开辟内存的类的表
        addClassTableEntry(cls);
    }
    // for future reference: shared cache never contains MH_BUNDLEs
    if (headerIsBundle) {
        cls->data()->flags |= RO_FROM_BUNDLE;
        cls->ISA()->data()->flags |= RO_FROM_BUNDLE;
    }
    return cls;
}

通过上述实现了把类和类名都加入到对应的表中,类中的属性、方法、协议等信息并未在此处理,经调试发现,无论是懒加载类还是非懒加载类都会执行realizeClassWithoutSwift函数,接下来我们直接看realizeClassWithoutSwift函数

  • 懒加载类为没有实现load方法的类
  • 非懒加载类为实现了load方法的类

realizeClassWithoutSwift
static Class realizeClassWithoutSwift(Class cls, Class previously)
{
    if (cls->isRealized()) {
        //内部会通过父类和元类递归调用此方法来设置,可以通过isRealized来区分,如果已经实现了防止持续递归
        return cls;
    }
    const char *mangledName = cls->mangledName();
    //从Mach- O里面读取到cls->data(),强制转换成class_ro_t结构
    auto ro = (const class_ro_t *)cls->data();
    auto isMeta = ro->flags & RO_META;
    if (ro->flags & RO_FUTURE) {
        // 元类、无需创建直接赋值
        rw = cls->data();
        ro = cls->data()->ro();
        ASSERT(!isMeta);
        cls->changeInfo(RW_REALIZED|RW_REALIZING, RW_FUTURE);
    } else {
        // 普通类、先创建
        rw = objc::zalloc<class_rw_t>();
        //内部会先判断是否有rwe,如果有就设置rwe,否则设置给ro
        rw->set_ro(ro);
        rw->flags = RW_REALIZED|RW_REALIZING|isMeta;
        cls->setData(rw);
    }
    cls->cache.initializeToEmptyOrPreoptimizedInDisguise();
    //设置bit
    if (isMeta) cls->cache.setBit(FAST_CACHE_META);
    // 为类选择一个索引, 如果没有索引则设置cls->instancesRequireRawIsa
    cls->chooseClassArrayIndex();
    //递归实现为父类赋值
    supercls = realizeClassWithoutSwift(remapClass(cls->getSuperclass()), nil);
    //递归实现为元类赋值
    metacls = realizeClassWithoutSwift(remapClass(cls->ISA()), nil);
    if (isMeta) {
        // 元类不需要non pointer ISA
        cls->setInstancesRequireRawIsa();
    } else {... //设置isa }
    // 更新父类和元类的映射
    cls->setSuperclass(supercls);
    cls->initClassIsa(metacls);
    if (supercls && !isMeta) reconcileInstanceVariables(cls, supercls, ro);
    // 设置实例大小
    cls->setInstanceSize(ro->instanceSize);
    // 设置flag
    if (ro->flags & RO_HAS_CXX_STRUCTORS) {
        cls->setHasCxxDtor();
        if (! (ro->flags & RO_HAS_CXX_DTOR_ONLY)) {
            cls->setHasCxxCtor();
        }
    }
    // 处理是否有关联对象
    if ((ro->flags & RO_FORBIDS_ASSOCIATED_OBJECTS) ||
        (supercls && supercls->forbidsAssociatedObjects()))
    {
        rw->flags |= RW_FORBIDS_ASSOCIATED_OBJECTS;
    }
    // 关联类和父类,确认继承关系
    if (supercls) {
        addSubclass(supercls, cls);
    } else {
        addRootClass(cls);
    }
    //Attach分类
    methodizeClass(cls, previously);
    return cls;
}

给class以及父类和元类对象的ro赋值以及给rw等成员赋值,最后调用methodizeClass;methodizeClass给方法排序,如果存在rwe,给rwe赋值,内部接着调用attachToClass函数:

static void methodizeClass(Class cls, Class previously)
{
    // 修复方法列表、属性列表和协议列表
    method_list_t *list = ro->baseMethods();
    if (list) {
        // 对方法进行排序
        prepareMethodLists(cls, &list, 1, YES, isBundleClass(cls), nullptr);
        if (rwe) rwe->methods.attachLists(&list, 1);
    }
    property_list_t *proplist = ro->baseProperties;
    if (rwe && proplist) {
        // attachLists主要作用是该方法会将新List按照原有顺序添加到旧的List前面并且扩容
        rwe->properties.attachLists(&proplist, 1);
    }
    protocol_list_t *protolist = ro->baseProtocols;
    if (rwe && protolist) {
        rwe->protocols.attachLists(&protolist, 1);
    }
    // 附加分类
    if (previously) { ... }
    objc::unattachedCategories.attachToClass(cls, cls,
                                             isMeta ? ATTACH_METACLASS : ATTACH_CLASS);
}

attachToClass内部判断是否可以调用attachCategories函数,关于分类相关的加载流程、下篇文章具体分析

void attachToClass(Class cls, Class previously, int flags)
{
    //attachCategories: 当且仅当主类不实现load,所有的分类均实现load的时候才会被调用
    if (it != map.end()) {
        category_list &list = it->second;
        if (flags & ATTACH_CLASS_AND_METACLASS) {
            int otherFlags = flags & ~ATTACH_CLASS_AND_METACLASS;
            attachCategories(cls, list.array(), list.count(), otherFlags | ATTACH_CLASS);
            attachCategories(cls->ISA(), list.array(), list.count(), otherFlags | ATTACH_METACLASS);
        } else {
            attachCategories(cls, list.array(), list.count(), flags);
        }
        map.erase(it);
    }
}

建议此文结合源码通过断点调试,以及文中注释一起查看、更容易理解;

总结

调用流程大致如下:map_images -> map_images_nolock -> _read_images -> 调用readClass以及realizeClassWithoutSwift等 -> methodizeClass -> attachToClass -> attachCategories ;主要几个步骤说明如下:

  • readClass:把类和类名都加入到对应的表中
  • realizeClassWithoutSwift:给class以及父类和元类对象的ro赋值给rw等成员赋值,经过此步后class中的ro以及rw都已经有值了,最后调用methodizeClass
  • methodizeClass:给方法排序,如果存在rwe,给rwe赋值,内部接着调用attachToClass函数
  • attachToClass:内部判断是否可以调用attachCategories函数
  • attachCategories:如果来到这里,内部通过attachLists函数将分类方法列表、属性列表、协议列表合并到原来的方法列表中
  • attachLists:主要是将新的list插入到旧的list的前面(内存平移以及内存拷贝的方式),此步函数具体实现下文分析;

本文部分内容来可能来源于网络,发布的内容如果侵犯了您的权益,请联系我们尽快删除!

相关文章
|
5月前
|
存储 算法 Java
JAVA程序运行问题之Java类加载到JVM中加载类时,实际上加载的是什么如何解决
JAVA程序运行问题之Java类加载到JVM中加载类时,实际上加载的是什么如何解决
|
5月前
|
前端开发 Java 编译器
Java面试题:描述Java类的加载过程,包括加载、链接、初始化等阶段。
Java面试题:描述Java类的加载过程,包括加载、链接、初始化等阶段。
34 0
|
存储 Java
【面试题精讲】JVM*类的生命周期*加载阶段
【面试题精讲】JVM*类的生命周期*加载阶段
|
安全 Java 编译器
【面试题精讲】JVM-类的生命周期-初始化阶段
【面试题精讲】JVM-类的生命周期-初始化阶段
|
Java 数据库连接 数据库
【JavaSE专栏60】静态代码块,Java类加载过程中执行的一段代码
【JavaSE专栏60】静态代码块,Java类加载过程中执行的一段代码
|
缓存 C++
12-objc_msgSend底层调用流程探究
12-objc_msgSend底层调用流程探究
64 0
|
Java 程序员
细说jvm(二)、java对象创建过程
细说jvm(二)、java对象创建过程
107 0
|
存储 安全 前端开发
【JVM原理探索】你真正掌握了Java对象创建的流程吗?
【JVM原理探索】你真正掌握了Java对象创建的流程吗?
170 0
【JVM原理探索】你真正掌握了Java对象创建的流程吗?
|
Java 编译器
【JVM原理探索】class字节码指令方法[调用]详解(上) | Java开发实战
【JVM原理探索】class字节码指令方法[调用]详解(上) | Java开发实战
149 0
【JVM原理探索】class字节码指令方法[调用]详解(上) | Java开发实战
|
机器学习/深度学习 Java
简述java的三种循环流程
简述java的三种循环流程
120 0