美文网首页
应用程序加载-- _objc_init 分析类的加载流程

应用程序加载-- _objc_init 分析类的加载流程

作者: 小溜子 | 来源:发表于2020-10-14 11:50 被阅读0次

    一、前言

    在启动 app 的时候, dyld 会对动态库进行加载、链接等一系列动作,之后就会来到 libobjc.A.dylib 库中调用 _objc_init 对类进行处理,通过 map_images 映射出整个镜像文件,再通过 read_images 加载镜像文件,此时类已经加载完成,那其中类的加载的流程又是怎么样的呢?类的属性、方法、协议都是怎么加载的呢?接下来就从 _objc_init 开始整篇文章的分析。附上 objc 源码 下载链接。

    二、_objc_init 源码分析

    void _objc_init(void)
    {
      static bool initialized = false;
      if (initialized) return;
      initialized = true;
      
      // 环境变量的一些操作
      environ_init();
      tls_init();
      // 系统级别的 c++ 构造函数调用
      static_init();
      // 空函数,预留
      lock_init();
      // 注册监听异常的回调
      exception_init();
      // 注册回调通知
      _dyld_objc_notify_register(&map_images, load_images, unmap_image);
    }
    
    1. environ_init 分析
      environ_init 方法是读取影响运行时的环境变量,最后内部有一段打印环境变量的代码。我将它提取出来,不做判断,打印结果如下。当然也可以在终端使用 export OBJC_HELP=1 指令打印环境变量。


      image.png

      我们可以在 Xcode 中修改环境变量的值来达到我们调试的一些目的。

    2.tls_init分析
    主要是对线程 key 的绑定。

    void tls_init(void)
    {
    #if SUPPORT_DIRECT_THREAD_KEYS
        _objc_pthread_key = TLS_DIRECT_KEY;
        pthread_key_init_np(TLS_DIRECT_KEY, &_objc_pthread_destroyspecific);
    #else
        _objc_pthread_key = tls_create(&_objc_pthread_destroyspecific);
    #endif
    }
    
    1. static_init 分析
      运行 C++ 静态构造函数,在 dyld 调用我们自定义构造函数之前。
    static void static_init()
    {
        size_t count;
        auto inits = getLibobjcInitializers(&_mh_dylib_header, &count);
        for (size_t i = 0; i < count; i++) {
            inits[i]();
        }
    }
    
    1. lock_init 分析
    void lock_init(void)
    {
    }
    
    1. exception_init 分析
      初始化 libobjc 库的异常处理系统,注册监听异常崩溃的回调,当发生崩溃时,就会来到 _objc_terminate 函数里面。
    void exception_init(void)
    {
        old_terminate = std::set_terminate(&_objc_terminate);
    }
    static void _objc_terminate(void)
    {
        if (PrintExceptions) {
            _objc_inform("EXCEPTIONS: terminating");
        }
    
        if (! __cxa_current_exception_type()) {
            // No current exception.
            (*old_terminate)();
        }
        else {
            // There is a current exception. Check if it’s an objc exception.
            @try {
                __cxa_rethrow();
            } @catch (id e) {
                // It’s an objc object. Call Foundation’s handler, if any.
                (*uncaught_handler)((id)e);
                (*old_terminate)();
            } @catch (...) {
                // It’s not an objc object. Continue to C++ terminate.
                (*old_terminate)();
            }
        }
    }
    

    三、_dyld_objc_notify_register 源码分析
    整个 objc 在这个里面是一个运行时环境,运行时环境去加载所有类的一些信息的时候,就会依赖这个注册函数的回调的通知,告诉当前的 dyld 的做了哪些事情,你需要哪些环境来进行彼此的通讯,比如当前的 map_images。
    1.知识预备:懒加载类和非懒加载类的区别

    简单来说就是有没有实现 load 方法,非懒加载类在类的内部实现了 load 方法,类的加载就会提前,而懒加载类没有实现 load 方法,在使用的第一次才会加载,当我们再给这个类的发送消息,如果是第一次,在消息查找的过程中就会判断这个类是否加载,没有加载就会加载这个类。

    1. map_images 分析
      map_images 方法在 image 加载到内存的时候会触发该方法的调用。忽略内部函数跳转、打印和操作 hCount 的代码,最终会来到 _read_images。

    2. _read_images 分析
      (1) _read_images 方法首先创建了两张表用来存储类。

    // 如果是第一次进来,就会走 if 下面的代码
        if (!doneOnce) {
            // 之后就不会来了
            // 为什么只来一次呢,因为第一次进来的时候,类,协议,sel,分类都没有
            // 需要创建容器来保存这些东西,这里创建的是两个表。
            doneOnce = YES;
            
            //... 忽略一些无关紧要的代码
    
            if (DisableTaggedPointers) {
                disableTaggedPointers();
            }
            // TaggedPointer 的优化处理
            initializeTaggedPointerObfuscator();
    
            // 4/3是 NXMapTable 的负载因子
            int namedClassesSize = 
                (isPreoptimized() ? unoptimizedTotalClasses : totalClasses) * 4 / 3;
            // 实例化存储类的哈希表,并根据当前类的数量做动态扩容
            // 只要你没有在共享缓存的类,不管实现或者未实现都会在这个里面
            gdb_objc_realized_classes =
                NXCreateMapTable(NXStrValueMapPrototype, namedClassesSize);
            // 已经被分配的类和元类都会放在这个表里
            allocatedClasses = NXCreateHashTable(NXPtrPrototype, 0, nil);
        }
    

    (2) readClass 分析

    for (EACH_HEADER) {
            // 从编译后的类列表中取出所有类,获取到的是一个classref_t类型的指针
            classref_t *classlist = _getObjc2ClassList(hi, &count);
            
            if (! mustReadClasses(hi)) {
                // 图像充分优化,我们不需要调用 readClass()
                continue;
            }
    
            bool headerIsBundle = hi->isBundle();
            bool headerIsPreoptimized = hi->isPreoptimized();
            
            for (i = 0; i < count; i++) {
                 // 数组中会取出OS_dispatch_queue_concurrent、OS_xpc_object、NSRunloop等系统类,例如CF、Fundation、libdispatch中的类。以及自己创建的类
                Class cls = (Class)classlist[I];
                
                // 通过 readClass 函数获取处理后的新类,内部主要操作 ro 和 rw 结构体
                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;
                }
            }
    }
    
    Class readClass(Class cls, bool headerIsBundle, bool headerIsPreoptimized)
    {
        const char *mangledName = cls->mangledName();
        // 如果某个 cls 的 superclass 是 weak-linked 的并且丢失了,则返回YES。
        if (missingWeakSuperclass(cls)) {
            if (PrintConnecting) {
                _objc_inform("CLASS: IGNORING class '%s' with "
                             "missing weak-linked superclass", 
                             cls->nameForLogging());
            }
            // 添加到重映射表里面,映射为 nil
            addRemappedClass(cls, nil);
            cls->superclass = nil;
            return nil;
        }
        
        //... 忽略一些无关紧要的代码
    
        cls->fixupBackwardDeployingStableSwift();
    
        Class replacing = nil;
        // 对未来的一些类的 ro 和 rw 的特殊处理,一般不会进去
        if (Class newCls = popFutureNamedClass(mangledName)) {
            // 将objc_class复制到future类的结构中
            // 保存future 类的 rw
            class_rw_t *rw = newCls->data();
            const class_ro_t *old_ro = rw->ro;
            memcpy(newCls, cls, sizeof(objc_class));
            rw->ro = (class_ro_t *)newCls->data();
            newCls->setData(rw);
            freeIfMutable((char *)old_ro->name);
            free((void *)old_ro);
            
            addRemappedClass(cls, newCls);
            replacing = cls;
            cls = newCls;
        }
        
        if (headerIsPreoptimized  &&  !replacing) {
            // 断言
            assert(getClassExceptSomeSwift(mangledName));
        } else {
            // 将 cls 加入到 gdb_objc_realized_classes 表里面去
            addNamedClass(cls, mangledName, replacing);
            // 将 cls 插入到 allocatedClasses 表里面去
            addClassTableEntry(cls);
        }
    
        // 供以后参考:共享缓存从不包含mh_bundle
        if (headerIsBundle) {
            cls->data()->flags |= RO_FROM_BUNDLE;
            cls->ISA()->data()->flags |= RO_FROM_BUNDLE;
        }
        // 到此时,这个类在整个表里就有了,返回
        return cls;
    }
    

    (3) remapClassRef

    // 主要是修复重映射 - !noClassesRemapped() 在这里为 false,所以一般走不进来
    // 将未映射Class和Super Class重映射,被remap的类都是非懒加载的类
    if (!noClassesRemapped()) {
        for (EACH_HEADER) {
            // 重映射Class,注意是从_getObjc2ClassRefs函数中取出类的引用
            Class *classrefs = _getObjc2ClassRefs(hi, &count);
            for (i = 0; i < count; i++) {
                remapClassRef(&classrefs[I]);
            }
            // fixme why doesn’t test future1 catch the absence of this?
            classrefs = _getObjc2SuperRefs(hi, &count);
            for (i = 0; i < count; i++) {
                remapClassRef(&classrefs[I]);
            }
        }
    }
    

    (4) sel_registerNameNoLock

    // 将所有SEL都注册到哈希表中,是另外一张哈希表
    static size_t UnfixedSelectors;
    {
         mutex_locker_t lock(selLock);
        for (EACH_HEADER) {
            if (hi->isPreoptimized()) continue;
            bool isBundle = hi->isBundle();
            SEL *sels = _getObjc2SelectorRefs(hi, &count);
            UnfixedSelectors += count;
            for (i = 0; i < count; i++) {
                // sel_cname 将 SEL 强转为 char 类型
                const char *name = sel_cname(sels[i]);
                // 注册 SEL 的操作
                sels[i] = sel_registerNameNoLock(name, isBundle);
            }
        }
    }
    

    (5) fixupMessageRef

    // 修复旧的函数指针调用遗留
    for (EACH_HEADER) {
       message_ref_t *refs = _getObjc2MessageRefs(hi, &count);
       if (count == 0) continue;
       if (PrintVtables) {
            _objc_inform("VTABLES: repairing %zu unsupported vtable dispatch "
                        "call sites in %s", count, hi->fname());
       }
       for (i = 0; i < count; i++) {
           // 内部将常用的 alloc、objc_msgSend 等函数指针进行注册,并 fix 为新的函数指针
           fixupMessageRef(refs+i);
       }
    }
    

    (6) readProtocol

    // 遍历所有协议列表,并且将协议列表加载到Protocol的哈希表中
    for (EACH_HEADER) {
        extern objc_class OBJC_CLASS_$_Protocol;
        // cls = Protocol类,所有协议和对象的结构体都类似,isa都对应Protocol类
        Class cls = (Class)&OBJC_CLASS_$_Protocol;
        assert(cls);
        // 获取protocol哈希表
        NXMapTable *protocol_map = protocols();
        bool isPreoptimized = hi->isPreoptimized();
        bool isBundle = hi->isBundle();
    
        // 从编译器中读取并初始化Protocol
        protocol_t **protolist = _getObjc2ProtocolList(hi, &count);
        for (i = 0; i < count; i++) {
            readProtocol(protolist[i], cls, protocol_map, 
                         isPreoptimized, isBundle);
        }
    }
    

    (7) remapProtocolRef

    // 修复协议列表引用,优化后的 images 可能是正确的,但是并不确定
    for (EACH_HEADER) {
        // 需要注意到是,下面的函数是 _getObjc2ProtocolRefs,和上面的 _getObjc2ProtocolList 不一样
        protocol_t **protolist = _getObjc2ProtocolRefs(hi, &count);
        for (i = 0; i < count; i++) {
            remapProtocolRef(&protolist[I]);
        }
    }
    

    (8) realizeClassWithoutSwift 分析
    初始化类就在这一步,首先将非懒加载类从 Mach-O 里面读取出来,然后通过 realizeClassWithoutSwift 实例化 rw。

    // 实现非懒加载的类,对于load方法和静态实例变量
    for (EACH_HEADER) {
        classref_t *classlist = 
            _getObjc2NonlazyClassList(hi, &count);
        for (i = 0; i < count; i++) {
            Class cls = remapClass(classlist[i]);
            if (!cls) continue;
            
            //... 忽略一些对 cls 的 cache 的一些操作
            
            addClassTableEntry(cls);
    
            //... 忽略无关紧要的代码
            
            // 实现所有非懒加载的类(实例化类对象的一些信息,例如rw)
            realizeClassWithoutSwift(cls);
        }
    }
    
    // 遍历 resolvedFutureClasses 数组,实现懒加载的类
    // resolvedFutureClasses 数组是在第二步的时候添加懒加载类的
    if (resolvedFutureClasses) {
        for (i = 0; i < resolvedFutureClassCount; i++) {
            Class cls = resolvedFutureClasses[I];
            if (cls->isSwiftStable()) {
                _objc_fatal("Swift class is not allowed to be future");
            }
            // 实现懒加载的类
            realizeClassWithoutSwift(cls);
            cls->setInstancesRequireRawIsa(false/*inherited*/);
        }
        free(resolvedFutureClasses);
    }  
    
    static Class realizeClassWithoutSwift(Class cls)
    {
        runtimeLock.assertLocked();
    
        const class_ro_t *ro;
        class_rw_t *rw;
        Class supercls;
        Class metacls;
        bool isMeta;
    
        if (!cls) return nil;
        // 判断 cls 是否已经初始化,里面是对 data()->flags 的判断
        if (cls->isRealized()) return cls;
        assert(cls == remapClass(cls));
    
        // 验证类不在共享缓存的未删除部分
        ro = (const class_ro_t *)cls->data();
        // 判断类是否是未实现的未来类
        if (ro->flags & RO_FUTURE) {
            // 是未来的类. rw 已经被初始化
            rw = cls->data();
            ro = cls->data()->ro;
            // 修改 flags
            cls->changeInfo(RW_REALIZED|RW_REALIZING, RW_FUTURE);
        } else {
            // 正常的类. 分配可写的类数据。
            // 开辟 rw 内存空间
            rw = (class_rw_t *)calloc(sizeof(class_rw_t), 1);
            rw->ro = ro;
            rw->flags = RW_REALIZED|RW_REALIZING;
            cls->setData(rw);
        }
        
        // 判断是否是元类
        isMeta = ro->flags & RO_META;
    
        rw->version = isMeta ? 7 : 0;  // old runtime went up to 6
    
    
        // 为这个类选择一个索引
        // 设置cls->instancesRequireRawIsa如果没有更多的索引可用
        cls->chooseClassArrayIndex();
    
        if (PrintConnecting) {
            _objc_inform("CLASS: realizing class '%s'%s %p %p #%u %s%s",
                         cls->nameForLogging(), isMeta ? " (meta)" : "", 
                         (void*)cls, ro, cls->classArrayIndex(),
                         cls->isSwiftStable() ? "(swift)" : "",
                         cls->isSwiftLegacy() ? "(pre-stable swift)" : "");
        }
    
        // 递归调用,实现父类和元类
        supercls = realizeClassWithoutSwift(remapClass(cls->superclass));
        metacls = realizeClassWithoutSwift(remapClass(cls->ISA()));
    
    #if SUPPORT_NONPOINTER_ISA
        // 禁用一些类和非指针isa
        bool instancesRequireRawIsa = cls->instancesRequireRawIsa();
        bool rawIsaIsInherited = false;
        static bool hackedDispatch = false;
         
        // 禁用非指针的 isa
        if (DisableNonpointerIsa) {
            // 非指针isa禁用的环境或应用程序SDK版本
            instancesRequireRawIsa = true;
        }
        else if (!hackedDispatch  &&  !(ro->flags & RO_META)  &&  
                 0 == strcmp(ro->name, "OS_object")) 
        {
            // 在 hackedDispatch 里 isa 也充当虚表指针
            hackedDispatch = true;
            instancesRequireRawIsa = true;
        }
        else if (supercls  &&  supercls->superclass  &&  
                 supercls->instancesRequireRawIsa()) 
        {
            // 从元类到根元类设置
            instancesRequireRawIsa = true;
            rawIsaIsInherited = true;
        }
        
        if (instancesRequireRawIsa) {
            cls->setInstancesRequireRawIsa(rawIsaIsInherited);
        }
    // SUPPORT_NONPOINTER_ISA
    #endif
    
        // 在重新映射时更新父类和元类
        cls->superclass = supercls;
        cls->initClassIsa(metacls);
    
        // 协调实例变量的偏移量/布局,可能会重新分配 class_ro_t,更新我们的 ro 变量。
        if (supercls  &&  !isMeta) reconcileInstanceVariables(cls, supercls, ro);
    
        // 如果还没有设置就开始设置 fastInstanceSize。
        cls->setInstanceSize(ro->instanceSize);
    
        // 将一些标志从 ro 复制到 rw
        if (ro->flags & RO_HAS_CXX_STRUCTORS) {
            cls->setHasCxxDtor();
            if (! (ro->flags & RO_HAS_CXX_DTOR_ONLY)) {
                cls->setHasCxxCtor();
            }
        }
        
        // 从ro或父类中传播关联的对象禁止标志
        if ((ro->flags & RO_FORBIDS_ASSOCIATED_OBJECTS) ||
            (supercls && supercls->forbidsAssociatedObjects()))
        {
            rw->flags |= RW_FORBIDS_ASSOCIATED_OBJECTS;
        }
    
        // 将这个类连接到它的父类的子类列表,即双向绑定
        if (supercls) {
            addSubclass(supercls, cls);
        } else {
            addRootClass(cls);
        }
    
        // 修复 cls 的方法列表、协议列表和属性列表,以及附加任何未完成的类别
        methodizeClass(cls);
        return cls;
    }
    

    (8) realizeClassWithoutSwift 分析

    初始化类就在这一步,首先将非懒加载类从 Mach-O 里面读取出来,然后通过 realizeClassWithoutSwift 实例化 rw。

    // 实现非懒加载的类,对于load方法和静态实例变量
    for (EACH_HEADER) {
        classref_t *classlist = 
            _getObjc2NonlazyClassList(hi, &count);
        for (i = 0; i < count; i++) {
            Class cls = remapClass(classlist[i]);
            if (!cls) continue;
            
            //... 忽略一些对 cls 的 cache 的一些操作
            
            addClassTableEntry(cls);
    
            //... 忽略无关紧要的代码
            
            // 实现所有非懒加载的类(实例化类对象的一些信息,例如rw)
            realizeClassWithoutSwift(cls);
        }
    }
    
    // 遍历 resolvedFutureClasses 数组,实现懒加载的类
    // resolvedFutureClasses 数组是在第二步的时候添加懒加载类的
    if (resolvedFutureClasses) {
        for (i = 0; i < resolvedFutureClassCount; i++) {
            Class cls = resolvedFutureClasses[I];
            if (cls->isSwiftStable()) {
                _objc_fatal("Swift class is not allowed to be future");
            }
            // 实现懒加载的类
            realizeClassWithoutSwift(cls);
            cls->setInstancesRequireRawIsa(false/*inherited*/);
        }
        free(resolvedFutureClasses);
    }  
    
    static Class realizeClassWithoutSwift(Class cls)
    {
        runtimeLock.assertLocked();
    
        const class_ro_t *ro;
        class_rw_t *rw;
        Class supercls;
        Class metacls;
        bool isMeta;
    
        if (!cls) return nil;
        // 判断 cls 是否已经初始化,里面是对 data()->flags 的判断
        if (cls->isRealized()) return cls;
        assert(cls == remapClass(cls));
    
        // 验证类不在共享缓存的未删除部分
        ro = (const class_ro_t *)cls->data();
        // 判断类是否是未实现的未来类
        if (ro->flags & RO_FUTURE) {
            // 是未来的类. rw 已经被初始化
            rw = cls->data();
            ro = cls->data()->ro;
            // 修改 flags
            cls->changeInfo(RW_REALIZED|RW_REALIZING, RW_FUTURE);
        } else {
            // 正常的类. 分配可写的类数据。
            // 开辟 rw 内存空间
            rw = (class_rw_t *)calloc(sizeof(class_rw_t), 1);
            rw->ro = ro;
            rw->flags = RW_REALIZED|RW_REALIZING;
            cls->setData(rw);
        }
        
        // 判断是否是元类
        isMeta = ro->flags & RO_META;
    
        rw->version = isMeta ? 7 : 0;  // old runtime went up to 6
    
    
        // 为这个类选择一个索引
        // 设置cls->instancesRequireRawIsa如果没有更多的索引可用
        cls->chooseClassArrayIndex();
    
        if (PrintConnecting) {
            _objc_inform("CLASS: realizing class '%s'%s %p %p #%u %s%s",
                         cls->nameForLogging(), isMeta ? " (meta)" : "", 
                         (void*)cls, ro, cls->classArrayIndex(),
                         cls->isSwiftStable() ? "(swift)" : "",
                         cls->isSwiftLegacy() ? "(pre-stable swift)" : "");
        }
    
        // 递归调用,实现父类和元类
        supercls = realizeClassWithoutSwift(remapClass(cls->superclass));
        metacls = realizeClassWithoutSwift(remapClass(cls->ISA()));
    
    #if SUPPORT_NONPOINTER_ISA
        // 禁用一些类和非指针isa
        bool instancesRequireRawIsa = cls->instancesRequireRawIsa();
        bool rawIsaIsInherited = false;
        static bool hackedDispatch = false;
         
        // 禁用非指针的 isa
        if (DisableNonpointerIsa) {
            // 非指针isa禁用的环境或应用程序SDK版本
            instancesRequireRawIsa = true;
        }
        else if (!hackedDispatch  &&  !(ro->flags & RO_META)  &&  
                 0 == strcmp(ro->name, "OS_object")) 
        {
            // 在 hackedDispatch 里 isa 也充当虚表指针
            hackedDispatch = true;
            instancesRequireRawIsa = true;
        }
        else if (supercls  &&  supercls->superclass  &&  
                 supercls->instancesRequireRawIsa()) 
        {
            // 从元类到根元类设置
            instancesRequireRawIsa = true;
            rawIsaIsInherited = true;
        }
        
        if (instancesRequireRawIsa) {
            cls->setInstancesRequireRawIsa(rawIsaIsInherited);
        }
    // SUPPORT_NONPOINTER_ISA
    #endif
    
        // 在重新映射时更新父类和元类
        cls->superclass = supercls;
        cls->initClassIsa(metacls);
    
        // 协调实例变量的偏移量/布局,可能会重新分配 class_ro_t,更新我们的 ro 变量。
        if (supercls  &&  !isMeta) reconcileInstanceVariables(cls, supercls, ro);
    
        // 如果还没有设置就开始设置 fastInstanceSize。
        cls->setInstanceSize(ro->instanceSize);
    
        // 将一些标志从 ro 复制到 rw
        if (ro->flags & RO_HAS_CXX_STRUCTORS) {
            cls->setHasCxxDtor();
            if (! (ro->flags & RO_HAS_CXX_DTOR_ONLY)) {
                cls->setHasCxxCtor();
            }
        }
        
        // 从ro或父类中传播关联的对象禁止标志
        if ((ro->flags & RO_FORBIDS_ASSOCIATED_OBJECTS) ||
            (supercls && supercls->forbidsAssociatedObjects()))
        {
            rw->flags |= RW_FORBIDS_ASSOCIATED_OBJECTS;
        }
    
        // 将这个类连接到它的父类的子类列表,即双向绑定
        if (supercls) {
            addSubclass(supercls, cls);
        } else {
            addRootClass(cls);
        }
    
        // 修复 cls 的方法列表、协议列表和属性列表,以及附加任何未完成的类别
        methodizeClass(cls);
        return cls;
    }
    

    methodizeClass 是将 ro 里面的方法、协议以及属性附加到 rw 里面和把分类中的方法、协议和属性添加到本类中,这也是分类是添加到本类中的时机,下面单独讲解一下这个方法。

    static void methodizeClass(Class cls)
    {
        runtimeLock.assertLocked();
        bool isMeta = cls->isMetaClass();
        auto rw = cls->data();
        auto ro = rw->ro;
    
        // 将 ro 里面的方法附加到 rw 里面去
        method_list_t *list = ro->baseMethods();
        if (list) {
            prepareMethodLists(cls, &list, 1, YES, isBundleClass(cls));
            rw->methods.attachLists(&list, 1);
        }
        // 将 ro 里面的属性附加到 rw 里面去
        property_list_t *proplist = ro->baseProperties;
        if (proplist) {
            rw->properties.attachLists(&proplist, 1);
        }
        // 将 ro 里面的协议附加到 rw 里面去
        protocol_list_t *protolist = ro->baseProtocols;
        if (protolist) {
            rw->protocols.attachLists(&protolist, 1);
        }
    
        // 根类获得额外的方法实现,如果它们还没有。这些适用于类别替换之前。
        if (cls->isRootMetaclass()) {
            // SEL SEL_initialize = NULL;
            addMethod(cls, SEL_initialize, (IMP)&objc_noop_imp, "", NO);
        }
    
        // Attach categories.
        // 返回类的未附加类别列表,并从列表中删除它们。
        category_list *cats = unattachedCategoriesForClass(cls, true /*realizing*/);
        // 将类别中的方法列表、属性和协议附加到类中
        attachCategories(cls, cats, false /*don’t flush caches*/);
    
        if (cats) free(cats);
        // ... 忽略一些无关紧要的代码
    }
    
    

    其中 attachLists 出现频率很高,基本上方法、协议、属性都是通过 attachLists 函数附加到对应的列表上的,接下来单独介绍一下这个方法。

    void attachLists(List* const * addedLists, uint32_t addedCount) {
            if (addedCount == 0) return;
    
            if (hasArray()) {
                // many lists -> many lists
                uint32_t oldCount = array()->count;
                uint32_t newCount = oldCount + addedCount;
                setArray((array_t *)realloc(array(), array_t::byteSize(newCount)));
                array()->count = newCount;
                // // 将 addedLists 移动到 array,memmove会对拷贝的数据作检查,确保内存没有覆盖,如果发现会覆盖数据,简单的实现是调转开始拷贝的位置,从尾部开始拷贝
                memmove(array()->lists + addedCount, array()->lists,
                        oldCount * sizeof(array()->lists[0]));
                // 将 addedLists 拷贝到 array()->lists,如果复制的两个区域存在重叠时使用memcpy,其结果是不可预知的,有可能成功也有可能失败的
                memcpy(array()->lists, addedLists, 
                       addedCount * sizeof(array()->lists[0]));
            }
            else if (!list  &&  addedCount == 1) {
                // 0 lists -> 1 list
                list = addedLists[0];
            } 
            else {
                // 1 list -> many lists,直接追到数组后面
                List* oldList = list;
                uint32_t oldCount = oldList ? 1 : 0;
                uint32_t newCount = oldCount + addedCount;
                setArray((array_t *)malloc(array_t::byteSize(newCount)));
                array()->count = newCount;
                if (oldList) array()->lists[addedCount] = oldList;
                memcpy(array()->lists, addedLists, 
                       addedCount * sizeof(array()->lists[0]));
            }
    }
    
    

    (9) addUnattachedCategoryForClass

    // 发现和处理所有Category
    for (EACH_HEADER) {
        // 外部循环遍历找到当前类,查找类对应的Category数组
        category_t **catlist = 
            _getObjc2CategoryList(hi, &count);
        bool hasClassProperties = hi->info()->hasCategoryClassProperties();
    
        for (i = 0; i < count; i++) {
            // 内部循环遍历当前类的所有Category
            category_t *cat = catlist[I];
            Class cls = remapClass(cat->cls);
    
            if (!cls) {
                // 类别的目标类丢失(可能是弱链接)
                catlist[i] = nil;
                if (PrintConnecting) {
                    _objc_inform("CLASS: IGNORING category \?\?\?(%s) %p with "
                                 "missing weak-linked target class", 
                                 cat->name, cat);
                }
                continue;
            }
    
            // 首先,通过其所属的类注册Category。如果这个类已经被实现,则重新构造类的方法列表。
            bool classExists = NO;
            if (cat->instanceMethods ||  cat->protocols  
                ||  cat->instanceProperties) 
            {
                // 将Category添加到对应Class的value中,value是Class对应的所有category数组
                addUnattachedCategoryForClass(cat, cls, hi);
                // 将Category的method、protocol、property添加到Class
                // 判断 cls 是否实现
                if (cls->isRealized()) {
                    remethodizeClass(cls);
                    classExists = YES;
                }
                if (PrintConnecting) {
                    _objc_inform("CLASS: found category -%s(%s) %s", 
                                 cls->nameForLogging(), cat->name, 
                                 classExists ? "on existing class" : "");
                }
            }
    
            // 这块和上面逻辑一样,区别在于这块是对Meta Class做操作,而上面则是对Class做操作
            // 根据下面的逻辑,从代码的角度来说,是可以对元类添加Category的
            if (cat->classMethods  ||  cat->protocols  
                ||  (hasClassProperties && cat->_classProperties)) 
            {
                addUnattachedCategoryForClass(cat, cls->ISA(), hi);
                if (cls->ISA()->isRealized()) {
                    remethodizeClass(cls->ISA());
                }
                if (PrintConnecting) {
                    _objc_inform("CLASS: found category +%s(%s)", 
                                 cls->nameForLogging(), cat->name);
                }
            }
        }
        
        // 初始化从磁盘中加载的所有类,发现Category必须是最后执行的
        // 从runtime DebugNonFragileIvars字段一直是 false,所以不会进入这个方法中
        if (DebugNonFragileIvars) {
            realizeAllClasses();
        }
        
        //... 忽略一些打印的代码
    }
    
    

    (10) 小拓展
    在 read_images 里经常出现 _getObjc2ClassRefs 之类的代码,这是从 Mach-O 文件里面读取相应 setion 段的数据,我们通过 MachOView 可以看到相关 setion 段的信息。


    image.png
    1. load_images 分析

    load_images 函数是对 load 方法的加载和调用,接下来我们就来看看底层是怎么对 load 处理的。

    void
    load_images(const char *path __unused, const struct mach_header *mh)
    {
        // 如果这里没有+load方法,则返回时不带锁。
        if (!hasLoadMethods((const headerType *)mh)) return;
    
        recursive_mutex_locker_t lock(loadMethodLock);
    
        {
            mutex_locker_t lock2(runtimeLock);
            // 准备 load 方法
            prepare_load_methods((const headerType *)mh);
        }
    
        // 调用 load 方法
        call_load_methods();
    }
    
    

    进到 prepare_load_methods 可以看到系统是如何加载 load 方法的。

    void prepare_load_methods(const headerType *mhdr)
    {
        size_t count, i;
        runtimeLock.assertLocked();
        // 获取非懒加载类列表
        classref_t *classlist = 
            _getObjc2NonlazyClassList(mhdr, &count);
        for (i = 0; i < count; i++) {
            // 循环遍历去加载非懒加载类的 load 方法到 loadable_classes
            schedule_class_load(remapClass(classlist[i]));
        }
        // 获取非懒加载分类列表
        category_t **categorylist = _getObjc2NonlazyCategoryList(mhdr, &count);
        for (i = 0; i < count; i++) {
            category_t *cat = categorylist[i];
            Class cls = remapClass(cat->cls);
            if (!cls) continue;  // category for ignored weak-linked class
            if (cls->isSwiftStable()) {
                _objc_fatal("Swift class extensions and categories on Swift "
                            "classes are not allowed to have +load methods");
            }
            // 如果本类没有初始化就去初始化
            realizeClassWithoutSwift(cls);
            assert(cls->ISA()->isRealized());
            
            // 循环遍历去加载非懒加载分类的 load 方法到 loadable_categories
            // 和非懒加载类差不多,就是数组不一样
            add_category_to_loadable_list(cat);
        }
    }
    
    static void schedule_class_load(Class cls)
    {
        if (!cls) return;
        assert(cls->isRealized());  // _read_images should realize
    
        if (cls->data()->flags & RW_LOADED) return;
    
        // 常规操作,递归调用父类加载 load 方法
        schedule_class_load(cls->superclass);
        // 将 load 方法加载到 loadable_classes
        add_class_to_loadable_list(cls);
        cls->setInfo(RW_LOADED); 
    }
    
    void add_class_to_loadable_list(Class cls)
    {
        IMP method;
        loadMethodLock.assertLocked();
        // 获取 load 方法的 imp
        method = cls->getLoadMethod();
        // 如果没有 load 方法直接返回
        if (!method) return; 
        
        if (PrintLoading) {
            _objc_inform("LOAD: class '%s' scheduled for +load", 
                         cls->nameForLogging());
        }
        // 扩容
        if (loadable_classes_used == loadable_classes_allocated) {
            loadable_classes_allocated = loadable_classes_allocated*2 + 16;
            loadable_classes = (struct loadable_class *)
                realloc(loadable_classes,
                                  loadable_classes_allocated *
                                  sizeof(struct loadable_class));
        }
        // loadable_classes 添加 load 方法
        loadable_classes[loadable_classes_used].cls = cls;
        loadable_classes[loadable_classes_used].method = method;
        loadable_classes_used++;
    }
    
    

    从上面准备 load 方法的代码,可以看出,是先去加载父类的,然后再加载本类,之后再去加载分类的 load 方法。我们再看看是怎么调用 load 方法的。

    void call_load_methods(void)
    {
        static bool loading = NO;
        bool more_categories;
        loadMethodLock.assertLocked();
    
        // 保证只调用一次
        if (loading) return;
        loading = YES;
    
        void *pool = objc_autoreleasePoolPush();
        // do while 循环调用 load 方法
        do {
            // 1.重复调用非懒加载类的 load,直到没有更多的
            while (loadable_classes_used > 0) {
                call_class_loads();
            }
    
            // 2.调用非懒加载分类的 load 方法,和非懒加载类差不多
            more_categories = call_category_loads();
    
            // 3. 如果有类或更多未尝试的类别,则运行更多 load
        } while (loadable_classes_used > 0  ||  more_categories);
    
        objc_autoreleasePoolPop(pool);
        loading = NO;
    }
    
    static void call_class_loads(void)
    {
        int i;
        // 取出 loadable_classes
        struct loadable_class *classes = loadable_classes;
        int used = loadable_classes_used;
        loadable_classes = nil;
        loadable_classes_allocated = 0;
        loadable_classes_used = 0;
        
        // 调用保存在 loadable_classes 里的 load 方法
        for (i = 0; i < used; i++) {
            Class cls = classes[i].cls;
            load_method_t load_method = (load_method_t)classes[i].method;
            if (!cls) continue; 
    
            if (PrintLoading) {
                _objc_inform("LOAD: +[%s load]\n", cls->nameForLogging());
            }
            // 发送 load 消息
            (*load_method)(cls, SEL_load);
        }
        
        // 释放内存
        if (classes) free(classes);
    }
    
    

    总结看 load 的调用,可以得出,load 的调用顺序是:父类->本类->分类。

    1. unmap_image 分析
      unmap_image 是用来处理将被 dyld 取消映射的给定 images。
    void 
    unmap_image(const char *path __unused, const struct mach_header *mh)
    {
        recursive_mutex_locker_t lock(loadMethodLock);
        mutex_locker_t lock2(runtimeLock);
        unmap_image_nolock(mh);
    }
    
    void 
    unmap_image_nolock(const struct mach_header *mh)
    {
        if (PrintImages) {
            _objc_inform("IMAGES: processing 1 newly-unmapped image...\n");
        }
    
        header_info *hi;
        
        // 查找映像的运行时 header_info 结构
        for (hi = FirstHeader; hi != NULL; hi = hi->getNext()) {
            if (hi->mhdr() == (const headerType *)mh) {
                break;
            }
        }
    
        if (!hi) return;
    
        if (PrintImages) {
            _objc_inform("IMAGES: unloading image for %s%s%s\n", 
                         hi->fname(),
                         hi->mhdr()->filetype == MH_BUNDLE ? " (bundle)" : "",
                         hi->info()->isReplacement() ? " (replacement)" : "");
        }
        // 目前只处理 MH_BUNDLE
        _unload_image(hi);
    
        // 从标题列表中删除 header_info
        removeHeader(hi);
        free(hi);
    }
    
    

    四、 总结

    1.类的加载会先来到 _objc_init 函数,执行 _dyld_objc_notify_register,再通过 dyld 的 registerObjCNotifiers 回调到 _dyld_objc_notify_register 并执行 map_images、load_images、unmap_image。
    2.map_images 加载镜像文件,_read_images 读取镜像文件并加载类。
    3.第一次调用 _read_images 的时候会去初始化两张表 gdb_objc_realized_classes 和 allocatedClasses 进行类信息的存储。
    4.初始化表之后就是调用 readClass 是将类插入到 allocatedClasses 表中的。
    5.然后再通过 remapClassRef 进行修复类的重映射和 fixupMessageRef 修复旧的函数指针调用遗留。
    6.类处理完之后就是添加协议和方法都注册到哈希表中,方便以后对其进行调用。
    7.再通过 realizeClassWithoutSwift 去初始化类,其中是对类的 rw 实例化,以及将 ro 数据赋值到 rw 上。
    8.load_images 是对 load 方法的处理以及调用,调用顺序是 父类->本类->分类,而有多个分类 load 的时候是根据编译顺序执行的。
    9.unmap_image 是用来处理将被 dyld 取消映射的给定 images。

    相关文章

      网友评论

          本文标题:应用程序加载-- _objc_init 分析类的加载流程

          本文链接:https://www.haomeiwen.com/subject/wdvvpktx.html