美文网首页iOSiOS 底层探索之路
iOS 底层探索:类的加载上(dyld和objc关联)

iOS 底层探索:类的加载上(dyld和objc关联)

作者: 欧德尔丶胡 | 来源:发表于2020-10-14 17:24 被阅读0次

    文集:iOS 底层探索之路

    前言

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

    准备工作

    • 了解: 动态链接器dyld的加载流程 :
        1. APP是由内核引导启动的,kernel内核做好所有准备工作后会得到线程入口及main入口,但是线程不会马上进入main入口,因为还要加载动态链接器(dyld),dyld会将入口点保存下来,等dyld加载完所有动态链接库等工作之后,再开始执行main函数。
        1. 系统kernel做好启动程序的初始准备后,交给dyld负责。dyld接手后,系统先读取 App 的可执行文件(Mach-O文件),从里面获取dyld的路径,然后加载dyld,dyld去初始化运行环境,开启缓存策略,配合 ImageLoader 将二进制文件按格式加载到内存,加载程序相关依赖库(其中也包含我们的可执行文件),并对这些库进行链接,最后调用每个依赖库的初始化方法,在这一步,runtime被初始化。当所有依赖库初始化后,轮到最后一位(程序可执行文件)进行初始化,在这时runtime会对项目中所有类进行类结构初始化,然后调用所有的load方法。最后dyld返回main()函数地址,main()函数被调用。基本流程如图: dyld的基本加载流程 .png
    • 在main函数之前有茫茫多的系统操作流程,这里只是做大概的思路整理,但是为了分析类的加载我们首先明确一点 main函数是我们App程序的入口点函数,在App的main函数之前,系统会首先对App的runtime运行环境,做了一系列的初始化操作,动态链接器dyld调用runtime入口函数,runtime的入口函数是_objc_init,它是在main函数之前被dyld调用的。而+load()方法,则是在main函数前被_objc_init调用,所以_objc_init就是我们研究探索类加载的切入点。

    一 、objc_init分析

    我们在iOS App中设置符号断点_objc_init,则在App启动时(进入main函数之前),会进入如下调用堆栈:


    objc_init源码如下:

    /***********************************************************************
    * _objc_init
    * Bootstrap initialization. Registers our image notifier with dyld.
    * Called by libSystem BEFORE library initialization time
    *启动初始化。注册我们的镜像通知与dyld。
    *在库初始化时间之前由libSystem调用
    **********************************************************************/
    
    // runtime + 类的信息
    void _objc_init(void)
    {
        static bool initialized = false;
        if (initialized) return;
        initialized = true;
        
        // 环境变量的一些操作
        environ_init();
    //对线程池进行初始化的
        tls_init();
      // 系统级别的 c++ 构造函数调用
        static_init();
          //runtime运行时环境初始化,里面主要是unattachedCategories、allocatedClasses -- 分类初始化
        runtime_init();
     //初始化libobjc的异常处理系统
        exception_init();
        //启动回调机制,通常这不会做什么,因为所有的初始化都是惰性的,但是对于某些进程,我们会迫不及待地加载trampolines dylib
      // cache缓存初始化
       cache_init();
     //启动回调机制
     _imp_implementationWithBlock_init();
      // 注册回调通知 &:引用类型函数 外面变了跟着变,load_images值类型只需要加载
        _dyld_objc_notify_register(&map_images, load_images, unmap_image);
    
    #if __OBJC2__
        didCallDyldNotifyRegister = true;
    #endif
    }
    
    
    1. environ_init
      方法是读取影响运行时的环境变量,最后内部有一段打印环境变量的代码。我将它提取出来,不做判断,打印结果如下。当然也可以在终端使用 export OBJC_HELP=1 指令打印环境变量;

    2. tls_init
      主要是对线程池进行初始化的;

    3. static_init
      运行 C++ 静态构造函数,在 dyld 调用我们自定义构造函数之前;

    4. runtime_init:runtime运行时环境初始化,里面操作是unattachedCategories、allocatedClasses(表的初始化)

    5. exception_init
      初始化 libobjc 库的异常处理系统,注册监听异常崩溃的回调,当发生崩溃时,就会来到 _objc_terminate 函数里面;

    6. cache_init: cache缓存初始化

    7. _imp_implementationWithBlock_init :启动回调机制,通常这不会做什么

    8. _dyld_objc_notify_register , dyld注册回调通知:仅供objc运行时使用,注册处理程序,以便在映射,取消映射和初始化objc镜像时调用。Dyld 将使用包含objc_image_info的镜像文件的数组回调"mapped"函数。

    接下来重点分析将dyld和objc关联的_dyld_objc_notify_register

    二 、_dyld_objc_notify_register分析 _dyld_objc_notify_register

    首先我们查看源码:

    //
    // Note: only for use by objc runtime
    // Register handlers to be called when objc images are mapped, unmapped, and initialized.
    // Dyld will call back the "mapped" function with an array of images that contain an objc-image-info section.
    // Those images that are dylibs will have the ref-counts automatically bumped, so objc will no longer need to
    // call dlopen() on them to keep them from being unloaded.  During the call to _dyld_objc_notify_register(),
    // dyld will call the "mapped" function with already loaded objc images.  During any later dlopen() call,
    // dyld will also call the "mapped" function.  Dyld will call the "init" function when dyld would be called
    // initializers in that image.  This is when objc calls any +load methods in that image.
    //
    // 翻译如下:
    
    //注意:仅供objc运行时使用
    //注册要映射,未映射和初始化objc图像的处理程序。
    // Dyld将使用包含objc-image-info节的图像数组回调“映射”函数。
    //这些是dylib的图像将自动增加引用计数,因此objc不再需要
    //对它们调用dlopen()以防止它们被卸载。 在调用_dyld_objc_notify_register()的过程中,
    // dyld将使用已加载的objc图像调用“映射”函数。 在以后的任何dlopen()调用期间,
    // dyld也将调用“映射”函数。 当调用dyld时,dyld将调用“ init”函数
    //该图片中的初始值设定项。 这是objc在该图像中调用任何+ load方法的时候。
    //
    void _dyld_objc_notify_register(_dyld_objc_notify_mapped    mapped,
                                    _dyld_objc_notify_init      init,
                                    _dyld_objc_notify_unmapped  unmapped);
    

    根据注释,我们可以知道,共注册了三个事件的回调:

    • _dyld_objc_notify_mapped : OC image被加载映射到内存(+load()方法在此时被调用)
    • _dyld_objc_notify_init : OC image被init时调用
    • _dyld_objc_notify_unmapped : OC image被移除内存时调用

    通过这个注释我们会发现,整个 objc 在这个里面是一个运行时环境,运行时环境去加载所有类的一些信息的时候,就会依赖这个注册函数的回调的通知,告诉当前的 dyld 的做了哪些事情,你需要哪些环境来进行彼此的通讯,所以这其实是一个跨库的通知,我们再去dyld3源码中去查找:

    在dyldAPIs.cpp 中
    
    void _dyld_objc_notify_register(_dyld_objc_notify_mapped    mapped,
                                    _dyld_objc_notify_init      init,
                                    _dyld_objc_notify_unmapped  unmapped)
    {
        dyld::registerObjCNotifiers(mapped, init, unmapped);
    }
    
    typedef void (*_dyld_objc_notify_mapped)(unsigned count, const char* const paths[], const struct mach_header* const mh[]);
    typedef void (*_dyld_objc_notify_init)(const char* path, const struct mach_header* mh);
    typedef void (*_dyld_objc_notify_unmapped)(const char* path, const struct mach_header* mh);
    
    

    跳转到 registerObjCNotifiers

    void registerObjCNotifiers(_dyld_objc_notify_mapped mapped, _dyld_objc_notify_init init, _dyld_objc_notify_unmapped unmapped)
    {
        // record functions to call
        sNotifyObjCMapped   = mapped; //&map_images
        sNotifyObjCInit     = init; // load_images
        sNotifyObjCUnmapped = unmapped; //unmap_image
    
        // call 'mapped' function with all images mapped so far
        try {
            notifyBatchPartial(dyld_image_state_bound, true, NULL, false, true);
        }
        catch (const char* msg) {
            // ignore request to abort during registration
        }
        
        // <rdar://problem/32209809> call 'init' function on all images already init'ed (below libSystem)
        for (std::vector<ImageLoader*>::iterator it=sAllImages.begin(); it != sAllImages.end(); it++) {
            ImageLoader* image = *it;
            if ( (image->getState() == dyld_image_state_initialized) && image->notifyObjC() ) {
                dyld3::ScopedTimer timer(DBG_DYLD_TIMING_OBJC_INIT, (uint64_t)image->machHeader(), 0, 0);
                (*sNotifyObjCInit)(image->getRealPath(), image->machHeader());
            }
        }
    }
    

    (1). 当image被dyld加载到内存后,会调用回调_dyld_objc_notify_mapped 。在runtime中,对应的函数是: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 方法在 image 加载到内存的时候会触发该方法的调用。忽略内部函数跳转、打印和操作 hCount 的代码,最终会来到 _read_images,其核心是用来读取Mach-O格式文件的runtime相关的section信息,并转化为runtime内部的数据结构。

    void 
    map_images_nolock(unsigned mhCount, const char * const mhPaths[],
                      const struct mach_header * const mhdrs[])
    { 
       ......
        if (hCount > 0) {
            _read_images(hList, hCount, totalClasses, unoptimizedTotalClasses);
        }
       ......
    }
    

    分析_read_images,由于其源码太多,所以只贴上部分代码:

    void _read_images(header_info **hList, uint32_t hCount, int totalClasses, int unoptimizedTotalClasses)
    {
    ...
    // 如果是第一次进来,就会走 if 下面的代码
        if (!doneOnce){...}
    // 将所有SEL都注册到哈希表中,是另外一张哈希表
     static size_t UnfixedSelectors;
        {
            mutex_locker_t lock(selLock);
            for (EACH_HEADER) {...}
       ts.log("IMAGE TIMES: fix up selector references");
    
        // Discover classes. Fix up unresolved future classes. Mark bundle classes.
        bool hasDyldRoots = dyld_shared_cache_some_image_overridden();
    
        // 从编译后的类列表中取出所有类,获取到的是一个classref_t类型的指针
        for (EACH_HEADER) {...}
    ts.log("IMAGE TIMES: discover classes");
    
        // 主要是修复重映射 - !noClassesRemapped() 在这里为 false,所以一般走不进来
        // 将未映射Class和Super Class重映射,被remap的类都是非懒加载的类
        if (!noClassesRemapped())  {...}
       ts.log("IMAGE TIMES: remap classes");
    
    #if SUPPORT_FIXUP
        // Fix up old objc_msgSend_fixup call sites
        // 修复旧的函数指针调用遗留
        for (EACH_HEADER){...}
    }
        ts.log("IMAGE TIMES: fix up objc_msgSend_fixup");
    #endif
        bool cacheSupportsProtocolRoots = sharedCacheSupportsProtocolRoots();
        // Discover protocols. Fix up protocol refs.
        // 遍历所有协议列表,并且将协议列表加载到Protocol的哈希表中
        for (EACH_HEADER){...}
      ts.log("IMAGE TIMES: discover protocols");
        // 修复协议列表引用,优化后的 images 可能是正确的,但是并不确定
        for (EACH_HEADER) {...}
      ts.log("IMAGE TIMES: fix up @protocol references");
        if (didInitialAttachCategories) {...}
      ts.log("IMAGE TIMES: discover categories");
        // 实现非懒加载的类,对于load方法和静态实例变量
        for (EACH_HEADER) {...}
      ts.log("IMAGE TIMES: realize non-lazy classes");
        // Realize newly-resolved future classes, in case CF manipulates them
        // 遍历 resolvedFutureClasses 数组,实现懒加载的类
        // resolvedFutureClasses 数组是在第二步的时候添加懒加载类的
        if (resolvedFutureClasses)  {...}
     ts.log("IMAGE TIMES: realize future classes");
    
        if (DebugNonFragileIvars) {
            realizeAllClasses();
        }
    
        // Print preoptimization statistics
        if (PrintPreopt){...}
    ...
    }
    

    _read_images大概过程: 4和9有关类的处理是我们关注的重点

    ·1∶条件控制进行一次的加载
    ·2∶修复预编译阶段的@selector的混乱问题
    ·3∶错误混乱的类处理
    ·4∶修复重映射一些没有被镜像文件加载进来的类
    ·5∶修复一些消息!
    ·6∶当我们类里面有协议的时候∶readProtocol
    ·7∶ 修复没有被加载的协议
    ·8∶分类处理
    ·9∶类的加载处理
    ·10∶没有被处理的类 优化那些被侵犯的类

    我们研究的核心是类的加载,所以接下来逐步分析重点内容:
    (1.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);
        }
    

    (1.2)sel_registerNameNoLock:修复预编译阶段的@selector的混乱问题分析

     // 修复预编译阶段的@selector的混乱问题
        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++) {
                    // sel_cname 将 SEL 强转为 char 类型
                    const char *name = sel_cname(sels[i]);
                    // 注册 SEL 的操作
                  //
                    SEL sel = sel_registerNameNoLock(name, isBundle);
                    if (sels[i] != sel) {
                        sels[i] = sel;
                    }
                }
            }
        }
    
    • _getObjc2SelectorRefs:其中_getObjc2SelectorRefs的源码如下,表示获取Mach-O中的静态段__objc_selrefs,后续通过_getObjc2开头的Mach-O静态段获取,都对应不同的section name
    • 其中SEL --> sel并不是简单的字符串,是带地址的字符串 如下所示,sels[i]与sel字符串一致,但是地址不一致,所以需要调整为一致的。即fix up
    //      function name                 content type     section name
    GETSECT(_getObjc2SelectorRefs,        SEL,             "__objc_selrefs"); 
    GETSECT(_getObjc2MessageRefs,         message_ref_t,   "__objc_msgrefs"); 
    GETSECT(_getObjc2ClassRefs,           Class,           "__objc_classrefs");
    GETSECT(_getObjc2SuperRefs,           Class,           "__objc_superrefs");
    GETSECT(_getObjc2ClassList,           classref_t const,      "__objc_classlist");
    GETSECT(_getObjc2NonlazyClassList,    classref_t const,      "__objc_nlclslist");
    GETSECT(_getObjc2CategoryList,        category_t * const,    "__objc_catlist");
    GETSECT(_getObjc2CategoryList2,       category_t * const,    "__objc_catlist2");
    GETSECT(_getObjc2NonlazyCategoryList, category_t * const,    "__objc_nlcatlist");
    GETSECT(_getObjc2ProtocolList,        protocol_t * const,    "__objc_protolist");
    GETSECT(_getObjc2ProtocolRefs,        protocol_t *,    "__objc_protorefs");
    GETSECT(getLibobjcInitializers,       UnsignedInitializer, "__objc_init_func");
    

    (1.3)重点:readClass 分析 :跟class有关了

      // Discover classes. Fix up unresolved future classes. Mark bundle classes.
        bool hasDyldRoots = dyld_shared_cache_some_image_overridden();
    
        // 从编译后的类列表中取出所有类,获取到的是一个classref_t类型的指针
        for (EACH_HEADER) {
            if (! mustReadClasses(hi, hasDyldRoots)) {
                // Image is sufficiently optimized that we need not call readClass()
                // 镜像充分优化,我们不需要调用 readClass()
                continue;
            }
    
            classref_t const *classlist = _getObjc2ClassList(hi, &count);
    
            bool headerIsBundle = hi->isBundle();
            bool headerIsPreoptimized = hi->hasPreoptimizedClasses();
    
            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;
                }
            }
        }
    
        ts.log("IMAGE TIMES: discover classes");
    
    
    /***********************************************************************
    * readClass
    * Read a class and metaclass as written by a compiler.
    * Returns the new class pointer. This could be: 
    * - cls
    * - nil  (cls has a missing weak-linked superclass)
    * - something else (space for this class was reserved by a future class)
    *
    * Note that all work performed by this function is preflighted by 
    * mustReadClasses(). Do not change this function without updating that one.
    *
    * Locking: runtimeLock acquired by map_images or objc_readClassPair
    **********************************************************************/
    Class readClass(Class cls, bool headerIsBundle, bool headerIsPreoptimized)
    {
        const char *mangledName = cls->mangledName();
    
        const char *LGPersonName = "LGPerson";
        if (strcmp(mangledName, LGPersonName)==0) {
            printf("%s -readClass!- %s \n",__func__,mangledName);
        }
        
        // 如果某个 cls 的 superclass 是 weak-linked 的并且丢失了,则返回YES。
        if (missingWeakSuperclass(cls)) {
            // No superclass (probably weak-linked). 
            // Disavow any knowledge of this subclass.
            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;
        if (Class newCls = popFutureNamedClass(mangledName)) { //这个流程暂时不做处理 可能在未来某个阶段处理
            // This name was previously allocated as a future class.
            // Copy objc_class to future class's struct.
            // Preserve future's rw data block.
            // 对未来新的一些类的 ro 和 rw 的特殊处理,一般不会进去
            if (newCls->isAnySwift()) {
                _objc_fatal("Can't complete future class request for '%s' "
                            "because the real class is too big.", 
                            cls->nameForLogging());
            }
            // 将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->set_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) {
            // class list built in shared cache
            // fixme strict assert doesn't work because of duplicates
            // ASSERT(cls == getClass(name));
           
            ASSERT(getClassExceptSomeSwift(mangledName));
        } else {
    // 重点----------------------------进到这里来----------------------------
    // 重点:从mach-o 文件读取插入到这两个表里面,自此在内存中就可以读到这个类了
            // 将 cls 加入到 gdb_objc_realized_classes 表里面去
            addNamedClass(cls, mangledName, replacing);
            // 将 cls 插入到 allocatedClasses 表里面去
            addClassTableEntry(cls);
        }
    
        // for future reference: shared cache never contains MH_BUNDLEs
        // 供以后参考:共享缓存从不包含mh_bundle
        if (headerIsBundle) {
            cls->data()->flags |= RO_FROM_BUNDLE;
            cls->ISA()->data()->flags |= RO_FROM_BUNDLE;
        }
        // 到此时,这个类在整个表里就有了,返回
        return cls;
    }
    
    

    注:
    // 重点:从mach-o 文件读取插入到这两个表里面,自此在内存中就可以读到这个类了
    // 将 cls 加入到 gdb_objc_realized_classes 表里面去
    addNamedClass(cls, mangledName, replacing);
    // 将 cls 插入到 allocatedClasses 表里面去
    addClassTableEntry(cls);

    (1.4) 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]);
                }
            }
        }
    
        ts.log("IMAGE TIMES: remap classes");
    

    (1.5) fixupMessageRef: 修复旧的函数指针调用遗留

       // Fix up old objc_msgSend_fixup call sites
        // 修复旧的函数指针调用遗留
        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);
            }
        }
    
        ts.log("IMAGE TIMES: fix up objc_msgSend_fixup");
    

    (1.6) readProtocol :读取并初始化Protocol

     bool cacheSupportsProtocolRoots = sharedCacheSupportsProtocolRoots();
        // Discover protocols. Fix up protocol refs.
        // 遍历所有协议列表,并且将协议列表加载到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->hasPreoptimizedProtocols();
    
            if (launchTime && isPreoptimized && cacheSupportsProtocolRoots) {
                if (PrintProtocols) {
                    _objc_inform("PROTOCOLS: Skipping reading protocols in image: %s",
                                 hi->fname());
                }
                continue;
            }
            bool isBundle = hi->isBundle();
            // 从编译器中读取并初始化Protocol
            protocol_t * const *protolist = _getObjc2ProtocolList(hi, &count);
            for (i = 0; i < count; i++) {
                readProtocol(protolist[i], cls, protocol_map, 
                             isPreoptimized, isBundle);
            }
        }
    
        ts.log("IMAGE TIMES: discover protocols");
    

    (1.7) fixupMessageRef :修复协议列表引用

       // 修复协议列表引用,优化后的 images 可能是正确的,但是并不确定
        for (EACH_HEADER) {
            // 需要注意到是,下面的函数是 _getObjc2ProtocolRefs,和上面的
            // At launch time, we know preoptimized image refs are pointing at the
            // shared cache definition of a protocol.  We can skip the check on
            // launch, but have to visit @protocol refs for shared cache images
            // loaded later.
            if (launchTime && cacheSupportsProtocolRoots && hi->isPreoptimized())
                continue;
            protocol_t **protolist = _getObjc2ProtocolRefs(hi, &count);
            for (i = 0; i < count; i++) {
                remapProtocolRef(&protolist[I]);
            }
        }
    
        ts.log("IMAGE TIMES: fix up @protocol references");
    

    (1.8)重点:realizeClassWithoutSwift :类的初始化

     // 实现非懒加载的类,对于load方法和静态实例变量
        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
                }
                // 实现所有非懒加载的类(实例化类对象的一些信息,例如rw)
               // alloc + init的前提是类的存在,就是从image镜像文件中读取并实现完毕之后的
                realizeClassWithoutSwift(cls, nil);
            }
        }
    
        ts.log("IMAGE TIMES: realize non-lazy classes");
    

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

    static Class realizeClassWithoutSwift(Class cls, Class previously)
    {
        runtimeLock.assertLocked();
    
        class_rw_t *rw;
        Class supercls;
        Class metacls;
    
        if (!cls) return nil;
        if (cls->isRealized()) return cls;
        // 判断 cls 是否已经初始化,里面是对 data()->flags 的判断
        ASSERT(cls == remapClass(cls));
    
        // fixme verify class is not in an un-dlopened part of the shared cache?
        // 验证类不在共享缓存的未删除部分
        auto ro = (const class_ro_t *)cls->data();
        // 判断类是否是未实现的未来类
        auto isMeta = ro->flags & RO_META;
        if (ro->flags & RO_FUTURE) {
            // 是未来的类. rw 已经被初始化
            rw = cls->data();
            ro = cls->data()->ro();
            ASSERT(!isMeta);
            // 修改 flags
            cls->changeInfo(RW_REALIZED|RW_REALIZING, RW_FUTURE);
        } else {
            // Normal class. Allocate writeable class data.
            // 正常的类. 分配可写的类数据。
            // 开辟 rw 内存空间
            rw = objc::zalloc<class_rw_t>();
            rw->set_ro(ro);
            rw->flags = RW_REALIZED|RW_REALIZING|isMeta;
            cls->setData(rw);
    // 注: rw ro rwe
    //
        }
        // 判断是否是元类
    #if FAST_CACHE_META
        if (isMeta) cls->cache.setBit(FAST_CACHE_META);
    #endif
    
        // Choose an index for this class.
        // Sets cls->instancesRequireRawIsa if indexes no more indexes are available
        // 设置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)" : "");
        }
        // Realize superclass and metaclass, if they aren't already.
        // This needs to be done after RW_REALIZED is set above, for root classes.
        // This needs to be done after class index is chosen, for root metaclasses.
        // This assumes that none of those classes have Swift contents,
        //   or that Swift's initializers have already been called.
        //   fixme that assumption will be wrong if we add support
        //   for ObjC subclasses of Swift classes.
        // 递归调用,实现父类和元类
        supercls = realizeClassWithoutSwift(remapClass(cls->superclass), nil);
        metacls = realizeClassWithoutSwift(remapClass(cls->ISA()), nil);
    
    #if SUPPORT_NONPOINTER_ISA
        if (isMeta) {
            // Metaclasses do not need any features from non pointer ISA
            // This allows for a faspath for classes in objc_retain/objc_release.
            cls->setInstancesRequireRawIsa();
        } else {
            // Disable non-pointer isa for some classes and/or platforms.
            // Set instancesRequireRawIsa.
            // 禁用一些类和非指针isa
            bool instancesRequireRawIsa = cls->instancesRequireRawIsa();
            bool rawIsaIsInherited = false;
            static bool hackedDispatch = false;
            // 禁用非指针的 isa
            if (DisableNonpointerIsa) {
                // Non-pointer isa disabled by environment or app SDK version
                // 非指针isa禁用的环境或应用程序SDK版本
                instancesRequireRawIsa = true;
            }
            else if (!hackedDispatch  &&  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->setInstancesRequireRawIsaRecursively(rawIsaIsInherited);
            }
        }
    // SUPPORT_NONPOINTER_ISA
    #endif
        // 在重新映射时更新父类和元类
        // Update superclass and metaclass in case of remapping
        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, previously);
    
        return cls;
    }
    
    

    (1.8.1) methodizeClass :方法的序列化

    //方法的序列化
    static void methodizeClass(Class cls, Class previously)
    {
        runtimeLock.assertLocked();
    
        bool isMeta = cls->isMetaClass();
        auto rw = cls->data();
        auto ro = rw->ro();
        auto rwe = rw->ext();
    
        // Methodizing for the first time
        if (PrintConnecting) {
            _objc_inform("CLASS: methodizing class '%s' %s", 
                         cls->nameForLogging(), isMeta ? "(meta)" : "");
        }
    
        // Install methods and properties that the class implements itself.
        // 将 ro 里面的方法附加到 rw 里面去
        method_list_t *list = ro->baseMethods();
        if (list) {
    // 准备好方法列表
            prepareMethodLists(cls, &list, 1, YES, isBundleClass(cls));
            if (rwe) rwe->methods.attachLists(&list, 1);
        }
        // 将 ro 里面的属性附加到 rw 里面去
        property_list_t *proplist = ro->baseProperties;
        if (rwe && proplist) {
            rwe->properties.attachLists(&proplist, 1);
        }
        // 将 ro 里面的协议附加到 rw 里面去
        protocol_list_t *protolist = ro->baseProtocols;
        if (rwe && protolist) {
            rwe->protocols.attachLists(&protolist, 1);
        }
    
        // 根类获得额外的方法实现,如果它们还没有。这些适用于类别替换之前。
        if (cls->isRootMetaclass()) {
            // root metaclass
            addMethod(cls, @selector(initialize), (IMP)&objc_noop_imp, "", NO);
        }
        // Attach categories.  附加分类
        if (previously) {
            if (isMeta) {
                objc::unattachedCategories.attachToClass(cls, previously,
                                                         ATTACH_METACLASS);
            } else {
                // When a class relocates, categories with class methods
                // may be registered on the class itself rather than on
                // the metaclass. Tell attachToClass to look for those.
                objc::unattachedCategories.attachToClass(cls, previously,
                                                         ATTACH_CLASS_AND_METACLASS);
            }
        }
        objc::unattachedCategories.attachToClass(cls, cls,
                                                 isMeta ? ATTACH_METACLASS : ATTACH_CLASS);
        ......
    }
    

    methodizeClass这个过程中attachLists方法进行解析:

    基本上方法、协议、属性都是通过 attachLists 函数附加到对应的列表上的
    void attachLists(List* const * addedLists, uint32_t addedCount) {
        if (addedCount == 0) return;
    
        if (hasArray()) {
            // many lists -> many lists
            //计算数组中旧lists的大小
            uint32_t oldCount = array()->count;
            //计算新的容量大小 = 旧数据大小+新数据大小
            uint32_t newCount = oldCount + addedCount;
            //根据新的容量大小,开辟一个数组,类型是 array_t,通过array()获取
            setArray((array_t *)realloc(array(), array_t::byteSize(newCount)));
            //设置数组大小
            array()->count = newCount;
            //旧的数据从 addedCount 数组下标开始 存放旧的lists,大小为 旧数据大小 * 单个旧list大小
            memmove(array()->lists + addedCount, array()->lists, 
                    oldCount * sizeof(array()->lists[0]));
            //新数据从数组 首位置开始存储,存放新的lists,大小为 新数据大小 * 单个list大小
            memcpy(
                   array()->lists, addedLists, 
                   addedCount * sizeof(array()->lists[0]));
        }
        else if (!list  &&  addedCount == 1) {
            // 0 lists -> 1 list
            list = addedLists[0];//将list加入mlists的第一个元素,此时的list是一个一维数组
        } 
        else {
            // 1 list -> many lists 有了一个list,有往里加很多list
            //新的list就是分类,来自LRU的算法思维,即最近最少使用
            //获取旧的list
            List* oldList = list;
            uint32_t oldCount = oldList ? 1 : 0;
            //计算容量和 = 旧list个数+新lists的个数
            uint32_t newCount = oldCount + addedCount;
            //开辟一个容量和大小的集合,类型是 array_t,即创建一个数组,放到array中,通过array()获取
            setArray((array_t *)malloc(array_t::byteSize(newCount)));
            //设置数组的大小
            array()->count = newCount;
            //判断old是否存在,old肯定是存在的,将旧的list放入到数组的末尾
            if (oldList) array()->lists[addedCount] = oldList;
            // memcpy(开始位置,放什么,放多大) 是内存平移,从数组起始位置存入新的list
            //其中array()->lists 表示首位元素位置
            memcpy(array()->lists, addedLists, 
                   addedCount * sizeof(array()->lists[0]));
        }
    }
    
    

    从源码可以得知,插入表主要分为三种情况:

    • 情况1:多对多】如果当前调用attachLists的list_array_tt二维数组中有多个一维数组

      • 计算数组中旧lists的大小

      • 计算新的容量大小 = 旧数据大小+新数据大小

      • 根据新的容量大小,开辟一个数组,类型是 array_t,通过array()获取

      • 设置数组大小

      • 旧的数据从 addedCount 数组下标开始 存放旧的lists,大小为 旧数据大小 * 单个旧list大小, 即整段平移,可以简单理解为原来的数据移动到后面,即指针偏移

      • 新数据从数组 首位置开始存储,存放新的lists,大小为 新数据大小 * 单个list大小,可以简单理解为越晚加进来,越在前面,越在前面,调用时则优先调用

    • 情况2:0对一】如果调用attachLists的list_array_tt二维数组为空且新增大小数目为 1

      • 直接赋值addedList的第一个list
    • 情况3:一对多】如果当前调用attachLists的list_array_tt二维数组只有一个一维数组

      • 获取旧的list

      • 计算容量和 = 旧list个数+新lists的个数

      • 开辟一个容量和大小的集合,类型是 array_t,即创建一个数组,放到array中,通过array()获取

      • 设置数组的大小

      • 判断old是否存在,old肯定是存在的,将旧的list放入到数组的末尾

      • memcpy(开始位置,放什么,放多大) 是内存平移,从数组起始位置开始存入新的list,其中array()->lists 表示首位元素位置

    • 针对情况3,这里的lists是指分类

    这是日常开发中,为什么子类实现父类方法会把父类方法覆盖的原因

    同理,对于同名方法,分类方法覆盖类方法的原因

    这个操作来自一个算法思维 LRU即最近最少使用,加这个newlist的目的是由于要使用这个newlist中的方法,这个newlist对于用户的价值要高,即优先调用

    会来到1对多的原因 ,主要是有分类的添加,即旧的元素在后面,新的元素在前面 ,究其根本原因主要是优先调用category,这也是分类的意义所在

    (2). dyld要init image的时候,会产生_dyld_objc_notify_init通知。在runime中, 是通过load_images方法做回调响应的。

    • load_images 函数是对 load 方法的加载和调用。

    (2.1) load_images 分析

    /***********************************************************************
    * load_images
    * Process +load in the given images which are being mapped in by dyld.
    *
    * Locking: write-locks runtimeLock and loadMethodLock
    **********************************************************************/
    extern bool hasLoadMethods(const headerType *mhdr);
    extern void prepare_load_methods(const headerType *mhdr);
    
    void
    load_images(const char *path __unused, const struct mach_header *mh)
    {
     
        if (!didInitialAttachCategories && didCallDyldNotifyRegister) {
            didInitialAttachCategories = true;
            loadAllCategories();
        }
    //1.判断镜像中是否有Load方法,没有直接返回
        // Return without taking locks if there are no +load methods here.
        if (!hasLoadMethods((const headerType *)mh)) return;
    
        recursive_mutex_locker_t lock(loadMethodLock);
     // 2.查找所有的Load方法
        // Discover load methods
        {
            mutex_locker_t lock2(runtimeLock);
            // 准备加载方法
            prepare_load_methods((const headerType *)mh);
        }
    
        // Call +load methods (without runtimeLock - re-entrant)
       // 3.调用所有Load方法
        call_load_methods();
    }
    

    (2.2) prepare_load_methods分析

    准备加载方法
    void prepare_load_methods(const headerType *mhdr)
    {
        size_t count, I;
    
        runtimeLock.assertLocked();
        // 获取非懒加载类列表
        classref_t const *classlist = 
            _getObjc2NonlazyClassList(mhdr, &count);
        for (i = 0; i < count; i++) {
            // 循环遍历去加载非懒加载类的 load 方法到 loadable_classes
            // schedule:安排,预定
            schedule_class_load(remapClass(classlist[i]));
        }
        // 获取非懒加载分类列表
        category_t * const *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, nil);
            ASSERT(cls->ISA()->isRealized());
            
            // 循环遍历去加载非懒加载分类的 load 方法到 loadable_categories
            // 和非懒加载类差不多,就是数组不一样
            add_category_to_loadable_list(cat);
        }
    }
    

    (2.2.1) schedule_class_load分析

    static void schedule_class_load(Class cls)
    {
        if (!cls) return;
        ASSERT(cls->isRealized());  // _read_images 必须实现
        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); 
    }
    
    • 可以看出,是先去加载父类的,然后再加载本类,之后再去加载分类的 load 方法

    (2.2.2)schedule_class_load分析

    /***********************************************************************
    * add_class_to_loadable_list
    * Class cls has just become connected. Schedule it for +load if
    * it implements a +load method.
    **********************************************************************/
    void add_class_to_loadable_list(Class cls)
    {
        IMP method;
    
        loadMethodLock.assertLocked();
    //    获取 load 方法的 imp
        method = cls->getLoadMethod();
    //    // 如果没有 load 方法直接返回
        if (!method) return;  // Don't bother if cls has no +load method
        
        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[loadable_classes_used].cls = cls;
        loadable_classes[loadable_classes_used].method = method;
        loadable_classes_used++;
    }
    

    (2.3) call_load_methods 分析

    void call_load_methods(void)
    {
        static bool loading = NO;
        bool more_categories;
    
        loadMethodLock.assertLocked();
    
        // Re-entrant calls do nothing; the outermost call will finish the job.
        // 保证只调用一次
        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;
    }
    

    (2.3.1) call_class_loads 分析

    static void call_class_loads(void)
    {
        int I;
        
        // Detach current loadable list.
        // 取出 loadable_classes
        struct loadable_class *classes = loadable_classes;
        int used = loadable_classes_used;
        loadable_classes = nil;
        loadable_classes_allocated = 0;
        loadable_classes_used = 0;
        
        // Call all +loads for the detached list.
        // 调用保存在 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, @selector(load));
        }
        
        // 释放内存
        if (classes) free(classes);
    }
    
    

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

    load_images方法整体调用过程


    (3). 当dyld要将image移除内存时,会发送_dyld_objc_notify_unmapped通知。在runtime中,是用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;
        
        // Find the runtime's header_info struct for the image
        // 查找映像的运行时 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);
    
        // Remove header_info from header list
        // 从标题列表中删除 header_info
        removeHeader(hi);
        free(hi);
    }
    

    三 、总结

    以上分析了_dyld_objc_notify_register这个在dyld和runtime中的跨库通知,在runtime中,分析了map_imagesload_imagesunmap_image方法实现

    _read_images几个重要的方法:

    • readClass :通过 readClass 函数获取处理后的新类,内部主要操作 ro 和 rw 结构体
    • realizeClassWithoutSwift:实现所有非懒加载的类
    • methodizeClass:方法的序列化
    • attachLists:方法、协议、属性基本上都是通过 这个方法 函数附加到对应的列表上的

    总结1 :map_images下的_read_images方法执行流程

    • 1.map_images加载镜像文件,_read_images读取镜像文件并加载类, 通过_getObjc2NonlazyClassList获取Mach-O的静态段__objc_nlclslist非懒加载类表。
    • 2.第一次调用_read_images 的时候会去初始化两张表 gdb_objc_realized_classesallocatedClasses 进行类信息的存储。
    • 3.初始化表之后就是调用readClass是将类插入到 allocatedClasses 表中的。
    • 4.然后再通过remapClassRef 进行修复类的重映射和fixupMessageRef修复旧的函数指针调用遗留。
    • 5.类处理完之后就是添加协议和方法都注册到哈希表中,方便以后对其进行调用。
    • 6.再通过 realizeClassWithoutSwift去初始化类,其中是对类的rw实例化,以及将ro 数据赋值到rw 上。

    总结2:load_images方法执行流程

    • 1.判断是否已经初始化分类,如果没有加载所有分类;
    • 2.判断镜像中是否有Load方法,没有直接返回,通过hasLoadMethods函数完成;
    • 3.查找所有的Load方法,通过prepare_load_methods函数完成;
    • 4.调用所有Load方法,通过call_load_methods函数完成。

    load_images是对 load 方法的处理以及调用,调用顺序是 父类->本类->分类,而有多个分类 load 的时候是根据编译顺序执行的

    总结3:unmap_image方法执行流程

    unmap_image 是用来处理将被 dyld 取消映射的给定 images

    在本篇文章中,我们知道dyld在main()函数之前,会调用runtime的_objc_init 方法。_objc_init是runtime的入口函数,通过跨库通知_dyld_objc_notify_register,实现类的加载,使dyld和objc关联。在上文提到过类的懒加载和非懒加载,下篇文章分析下,类在懒加载和非懒加载两种情况下有啥不同。

    四 、拓展:关于class_ro_tclass_rw_t

    类的结构以前分析过的

    struct objc_class : objc_object {   
        // Class ISA;
        Class superclass;
        cache_t cache;             // formerly cache pointer and vtable
        class_data_bits_t bits;    // class_rw_t * plus custom rr/alloc flags
    
        class_rw_t *data() const {
            return bits.data();
        }
        void setData(class_rw_t *newData) {
            bits.setData(newData);
        }
        ........
    
     struct class_rw_t {
        // Be warned that Symbolication knows the layout of this structure.
        uint32_t flags;
        uint16_t witness;
    #if SUPPORT_INDEXED_ISA
        uint16_t index;
    #endif
    
        explicit_atomic<uintptr_t> ro_or_rw_ext;
    
        Class firstSubclass;
        Class nextSiblingClass;
    
      .........
    
    struct class_ro_t {
        uint32_t flags;
        uint32_t instanceStart;
        uint32_t instanceSize;
    #ifdef __LP64__
        uint32_t reserved;
    #endif
    
        const uint8_t * ivarLayout;
        
        const char * name;
        method_list_t * baseMethodList;
        protocol_list_t * baseProtocols;
        const ivar_list_t * ivars;
    
        const uint8_t * weakIvarLayout;
        property_list_t *baseProperties;
    
        ...........
    

    class_ro_t存储了当前类在编译期就已经确定的属性、方法以及遵循的协议,里面是没有分类的方法的。那些运行时添加的方法将会存储在运行时生成的class_rw_t中。

    ro即表示read only,是无法进行修改的。

    ObjC 类中的属性、方法还有遵循的协议等信息都保存在 class_rw_t

    class_rw_t生成时机

    class_rw_t生成在运行时,在编译期间,class_ro_t结构体就已经确定,objc_class中的bits的data部分存放着该结构体的地址。在runtime运行之后,具体说来是在运行runtime的realizeClass 方法时,会生成class_rw_t结构体,该结构体包含了class_ro_t,并且更新data部分,换成class_rw_t结构体的地址。

    区别:

    class_ro_t存放的是编译期间就确定的;
    class_rw_t是在runtime时才确定,它会先将class_ro_t的内容拷贝过去,然后再将当前类的分类的这些属性、方法等拷贝到其中。
    所以可以说class_rw_tclass_ro_t的超集,当然实际访问类的方法、属性等也都是访问的class_rw_t中的内容

    相关文章

      网友评论

        本文标题:iOS 底层探索:类的加载上(dyld和objc关联)

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