美文网首页
十 类的加载-前篇 (map_images)

十 类的加载-前篇 (map_images)

作者: 可乐冒气 | 来源:发表于2021-01-03 22:36 被阅读0次

    前言

    前篇 我们了解到 应用的加载流程,在main 函数调用之前有许多系统的操作流程,但是在分析类的加载需要明确的是main 函数才是我们app程序的入口函数,我们知道 在执行main函数之前,系统会对runtime进行初始化,在之前我们了解到dyld会调用 runtime的入口函数,runtime的入口函数 就是 _objc_iinnit,它就是在 mian 函数之前就被dyld调用,而load方法就在 _objc_init中调用,所以 我们探究的起点就是 _objc_init函数

    image.png

    一 _objc_init 探究分析

    我们在runtime的源码中 可以了解到_objc_init 的源代码

    /***********************************************************************
    * _objc_init
    * Bootstrap initialization. Registers our image notifier with dyld.
    * Called by libSystem BEFORE library initialization time
    **********************************************************************/
    
    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(); //主要是运行时的初始化,主要分为两部分:分类初始化(unattachedCategories)和类的表初始化(allocatedClasses)
        exception_init(); // 初始化libobjc异常处理
        cache_init(); //缓存初始化
        _imp_implementationWithBlock_init(); //启动机制回调
        // 注册回调通知
        _dyld_objc_notify_register(&map_images, load_images, unmap_image);
    
    #if __OBJC2__
        didCallDyldNotifyRegister = true;
    #endif
    }·  
    

    二 _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 runtime 中使用
    // 注册回调函数, 当 objc 库 映射,未映射,和初始化的时候调用
    // dyld将要调用包含objc-image-info的镜像数组回调的“mapped”函数。
    // dyld 会为这些镜像自动增加引用技术,所以objc 不需要使用dlopen函数保证它不被卸载。
    // 在调用 `_dyld_objc_notify_register` 的过程中,dyld 会调用已经加载的镜像的 mapped 函数
    // 在之后的任何dlopen()调用期间,dyld也将调用“映射”函数。
    // 在dyld 调用镜像的initializers 的时候,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_register 实现

    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);
    }
    
    

    注册回调通知
    参数介绍

    • _dyld_objc_notify_mapped : dyld 将 image 加载进内存时 , 会触发该函数.
    • _dyld_objc_notify_init : dyld 初始化 image 会触发该方法. ( 我们所熟知的 load 方法也是在此处调用 ) .
    • unmap__dyld_objc_notify_unmapped: dyld 将 image 移除时 , 会触发该函数 .

    这三个回调函数 在runtime 中分别对应着 map_images, load_images, unmap_image

    三 map_images 将image 加载进内存回调

    • map_images
    /***********************************************************************
    * map_images
    * Process the given images which are being mapped in by dyld.
    * Calls ABI-agnostic code after taking ABI-specific locks.
    *
    * Locking: write-locks runtimeLock
    **********************************************************************/
    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 加载到内存的时候会触发该方法的调用

    • map_images_nolock
    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);
        }
    
        firstTime = NO;
        
        // Call image load funcs after everything is set up.
        for (auto func : loadImageFuncs) {
            for (uint32_t i = 0; i < mhCount; i++) {
                func(mhdrs[i]);
            }
        }
    }
    

    四 _read_images 读取镜像

    主要内容是:条件控制只执行一次、处理编译阶段SEL混乱的问题、错误的类处理、加载协议、分类处理、类的读取、以及针对某些非懒加载的在直接进行实现。

    我们现在分段进行解析,通过 ts.log进行分段分析

    4.1 创建表

    ... 省略
    if (!doneOnce) {
        doneOnce = YES;
        ...省略
        
        // namedClasses
        // Preoptimized classes don't go in this table.
        // 4/3 is NXMapTable's load factor
        int namedClassesSize = 
            (isPreoptimized() ? unoptimizedTotalClasses : totalClasses) * 4 / 3;
    // 实例化存储类的哈希表
        gdb_objc_realized_classes =
            NXCreateMapTable(NXStrValueMapPrototype, namedClassesSize);
            
        ts.log("IMAGE TIMES: first time tasks");
    }
    
    • gdb_objc_realized_classes存储不在共享缓存且已命名的所有类,其容量是类数量的4/3
    // This is a misnomer: gdb_objc_realized_classes is actually a list of 
    // named classes not in the dyld shared cache, whether realized or not.
    NXMapTable *gdb_objc_realized_classes;  // exported for debuggers in objc-gdb.h
    

    4.2 sel_registerNameNoLock 处理编译阶段SEL混乱的问题

     // 修复预编译阶段的@selector的混乱问题
     static size_t UnfixedSelectors;
      {
            mutex_locker_t lock(selLock);
            for (EACH_HEADER) {
                if (hi->hasPreoptimizedSelectors()) continue;
    
                bool isBundle = hi->isBundle();
                // 从macho 文件中 获取方法名称
                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;
                    }
                }
            }
        }
        ts.log("IMAGE TIMES: fix up selector references");
    
    • _getObjc2SelectorRefs:其中_getObjc2SelectorRefs的源码如下,表示获取Mach-O中的静态段__objc_selrefs
    //      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");
    
    • sel_cname:其中SEL --> sel并不是简单的字符串,是带地址的字符串 如下所示,sels[i]与sel字符串一致,但是地址不一致,所以需要调整为一致的。即fix up
      image.png

    4.3 readClass 重点

        // 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");
    
    • read_class
    
    /***********************************************************************
    * 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)
    {
        // 已经实现类从内存中读取,未实现的类从 mach-o 中读取
        const char *mangledName = cls->mangledName();
     // 测试代码
        const char *LGPersonName = "LGPerson";
        if (strcmp(mangledName, LGPersonName) == 0) {
            printf("%s ---- %s",__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.
            
            if (newCls->isAnySwift()) {
                _objc_fatal("Can't complete future class request for '%s' "
                            "because the real class is too big.", 
                            cls->nameForLogging());
            }
            
            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
        if (headerIsBundle) {
            cls->data()->flags |= RO_FROM_BUNDLE;
            cls->ISA()->data()->flags |= RO_FROM_BUNDLE;
        }
        // 返回这个类
        return cls;
    }
    
    • addNamedClass
      将当前类添加到已创建好的gdb_objc_realized_classes哈希表(存放所有类)
    /***********************************************************************
    * addNamedClass
    * Adds name => cls to the named non-meta class map.
    * Warns about duplicate class names and keeps the old mapping.
    * Locking: runtimeLock must be held by the caller
    **********************************************************************/
    static void addNamedClass(Class cls, const char *name, Class replacing = nil)
    {
        runtimeLock.assertLocked();
        Class old;
        if ((old = getClassExceptSomeSwift(name))  &&  old != replacing) {
            inform_duplicate(name, old, cls);
    
            // getMaybeUnrealizedNonMetaClass uses name lookups.
            // Classes not found by name lookup must be in the
            // secondary meta->nonmeta table.
            addNonMetaClass(cls);
        } else {
          // 将类名插入类表中
            NXMapInsert(gdb_objc_realized_classes, name, cls);
        }
        ASSERT(!(cls->data()->flags & RO_META));
    
        // wrong: constructed classes are already realized when they get here
        // ASSERT(!cls->isRealized());
    }
    
    • addClassTableEntry
      当前类已经初始化,所以要添加到allocatedClasses哈希表,并添加元类
    /***********************************************************************
    * addClassTableEntry
    * Add a class to the table of all classes. If addMeta is true,
    * automatically adds the metaclass of the class as well.
    * Locking: runtimeLock must be held by the caller.
    **********************************************************************/
    static void
    addClassTableEntry(Class cls, bool addMeta = true)
    {
        runtimeLock.assertLocked();
    
        // This class is allowed to be a known class via the shared cache or via
        // data segments, but it is not allowed to be in the dynamic table already.
        // 将本类 和 元类添加到类表中
        auto &set = objc::allocatedClasses.get();
    
        ASSERT(set.find(cls) == set.end());
        // 未识别的类
        if (!isKnownClass(cls))
            set.insert(cls);
        if (addMeta)
            addClassTableEntry(cls->ISA(), false);
        // 开辟空间-元类递归-元类开辟空间-元类名称插入类表
    }
    
    • 注意: 第三步 类处理(readClass)中 内部并没有完成类的加载,只是将类名 和 类地址 记录到了类表中

    4.4 修复重映射

    // 将未映射Class和Super Class重映射,被remap的类都是非懒加载的类

        // Fix up remapped classes
        // Class list and nonlazy class list remain unremapped.
        // Class refs and super refs are remapped for message dispatching.
        
        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");
    

    4.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");
    

    4.6 readProtocol :读取并初始化Protocol

    
        bool cacheSupportsProtocolRoots = sharedCacheSupportsProtocolRoots();
    
        // Discover protocols. Fix up protocol refs.
    // 遍历所有协议列表,并且将协议列表加载到Protocol的哈希表中
        for (EACH_HEADER) {
            extern objc_class OBJC_CLASS_$_Protocol;
            // 获取协议类 cls = Protocol类
            Class cls = (Class)&OBJC_CLASS_$_Protocol;
            ASSERT(cls);
        // 获取protocol哈希表
            NXMapTable *protocol_map = protocols();
            bool isPreoptimized = hi->hasPreoptimizedProtocols();
    
            // Skip reading protocols if this is an image from the shared cache
            // and we support roots
            // Note, after launch we do need to walk the protocol as the protocol
            // in the shared cache is marked with isCanonical() and that may not
            // be true if some non-shared cache binary was chosen as the canonical
            // definition
            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");
    
    

    4.7 修复协议列表引用

    // Fix up @protocol references
        // Preoptimized images may have the right 
        // answer already but we don't know for sure.
       // 修复协议列表引用,优化后的images可能是正确的,但是并不确定
        for (EACH_HEADER) {
            // 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;
    // 需要注意到是,下面的函数是_getObjc2ProtocolRefs,和上面的_getObjc2ProtocolList不一样
            protocol_t **protolist = _getObjc2ProtocolRefs(hi, &count);
            for (i = 0; i < count; i++) {
                remapProtocolRef(&protolist[i]);
            }
        }
    
        ts.log("IMAGE TIMES: fix up @protocol references");
    

    4.8 分类的加载

        // Discover categories. Only do this after the initial category
        // attachment has been done. For categories present at startup,
        // discovery is deferred until the first load_images call after
        // the call to _dyld_objc_notify_register completes. rdar://problem/53119145
        // 发现分类。仅在完成初始类别附件后才执行此操作。 对于启动时出现的类别,发现将推迟到对_dyld_objc_notify_register的调用完成后的第一个load_images调用为止。
        if (didInitialAttachCategories) {
            for (EACH_HEADER) {
                load_categories_nolock(hi);
            }
        }
    
        ts.log("IMAGE TIMES: discover categories");
    

    4.9 非懒加载的类

    非懒加载类定义:实现了+load方法 和 静态初始化的类是非懒加载类,否则就是懒加载类

    
        // Category discovery MUST BE Late to avoid potential races
        // when other threads call the new category code before
        // this thread finishes its fixups.
    
        // +load handled by prepare_load_methods()
    
        // Realize non-lazy classes (for +load methods and static instances)
        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)
                realizeClassWithoutSwift(cls, nil);
            }
        }
    
        ts.log("IMAGE TIMES: realize non-lazy classes");
    
    • _getObjc2NonlazyClassList获取到__objc_nlclslist,取出非懒加载类

    • addClassTableEntry 再加载一遍——如果已添加就不会添加进去,确保整个结构都被添加

    • realizeClassWithoutSwift是接下来要关注的地方

    • realizeClassWithoutSwift 分析

    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;
    }
    
    
    • methodizeClass 分析
    
    /***********************************************************************
    * methodizeClass
    * Fixes up cls's method list, protocol list, and property list.
    * Attaches any outstanding categories.
    * Locking: runtimeLock must be held by the caller
    **********************************************************************/
    //方法的序列化
    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);
        }
    
        // Root classes get bonus method implementations if they don't have 
        // them already. These apply before category replacements.
        // 根类获得额外的方法实现,如果它们还没有。这些适用于类别替换之前。
        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);
    
    #if DEBUG
        // Debug: sanity-check all SELs; log method list contents
        for (const auto& meth : rw->methods()) {
            if (PrintConnecting) {
                _objc_inform("METHOD %c[%s %s]", isMeta ? '+' : '-', 
                             cls->nameForLogging(), sel_getName(meth.name));
            }
            ASSERT(sel_registerName(sel_getName(meth.name)) == meth.name); 
        }
    #endif
    }
    
    • 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]));
        }
    }
    
    
    map_images.png

    相关文章

      网友评论

          本文标题:十 类的加载-前篇 (map_images)

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