美文网首页
类的加载

类的加载

作者: 凯歌948 | 来源:发表于2021-01-21 22:07 被阅读0次

    本文的主要目的是理解类的相关信息是如何加载到内存的,其中重点关注map_images和load_images

    objc类的加载.png

    map_images源码流程

    map_images方法的主要作用是将Mach-O中的类信息加载到内存。
    通过_objc_init --> map_images --> map_images_nolock --> _read_images,进入_read_images的源码。
    _read_images主要是加载类信息,即类、分类、协议等,进入_read_images源码实现,主要分为以下几部分:
    1、条件控制进行的一次加载
    2、修复预编译阶段的@selector的混乱问题
    3、错误混乱的类处理
    4、修复重映射一些没有被镜像文件加载进来的类
    5、修复一些消息
    6、当类里面有协议时:readProtocol 读取协议
    7、修复没有被加载的协议
    8、分类处理
    9、类的加载处理
    10、没有被处理的类,优化那些被侵犯的类

    /***********************************************************************
    * _read_images
    * Perform initial processing of the headers in the linked 
    * list beginning with headerList. 
    *
    * Called by: map_images_nolock
    *
    * Locking: runtimeLock acquired by map_images
    **********************************************************************/
    void _read_images(header_info **hList, uint32_t hCount, int totalClasses, int unoptimizedTotalClasses)
    {
        header_info *hi;
        uint32_t hIndex;
        size_t count;
        size_t i;
        Class *resolvedFutureClasses = nil;
        size_t resolvedFutureClassCount = 0;
        static bool doneOnce;
        bool launchTime = NO;
        TimeLogger ts(PrintImageTimes);
    
        runtimeLock.assertLocked();
    
    #define EACH_HEADER \
        hIndex = 0;         \
        hIndex < hCount && (hi = hList[hIndex]); \
        hIndex++
    
        if (!doneOnce) {
            doneOnce = YES;
            launchTime = YES;
    
    //省略。。。。。。
    
            if (DisableTaggedPointers) {
                disableTaggedPointers();
            }
            
            initializeTaggedPointerObfuscator();
    
            if (PrintConnecting) {
                _objc_inform("CLASS: found %d classes during launch", totalClasses);
            }
    
            // namedClasses
            // Preoptimized classes don't go in this table.
            // 4/3 is NXMapTable's load factor
            int namedClassesSize = 
                (isPreoptimized() ? unoptimizedTotalClasses : totalClasses) * 4 / 3;
            //创建表(哈希表key-value),目的是查找快
            //查看gdb_objc_realized_classes的注释说明,这个哈希表用于存储不在共享缓存且已命名类,无论类是否实现,其容量是类数量的4/3
            gdb_objc_realized_classes =
                NXCreateMapTable(NXStrValueMapPrototype, namedClassesSize);
    
            ts.log("IMAGE TIMES: first time tasks");
        }
    
        // Fix up @selector references 修复@selector引用
        //sel 不是简单的字符串,而是带地址的字符串
        static size_t UnfixedSelectors;
        {
            mutex_locker_t lock(selLock);
            for (EACH_HEADER) {
                if (hi->hasPreoptimizedSelectors()) continue;
    
                bool isBundle = hi->isBundle();
                //通过_getObjc2SelectorRefs拿到Mach-O中的静态段__objc_selrefs
                SEL *sels = _getObjc2SelectorRefs(hi, &count);
                UnfixedSelectors += count;
                for (i = 0; i < count; i++) {//列表遍历
                    const char *name = sel_cname(sels[i]);
                    //注册sel操作,即将sel插入namedSelectors哈希表
                    SEL sel = sel_registerNameNoLock(name, isBundle);
                    //sels[i]与sel字符串一致,但是地址不一致,所以需要调整为一致的。即fix up
                    if (sels[i] != sel) {
                        sels[i] = sel;
                    }
                }
            }
        }
    
        ts.log("IMAGE TIMES: fix up selector references");
        //3、错误混乱的类处理
        // Discover classes. Fix up unresolved future classes. Mark bundle classes.
        bool hasDyldRoots = dyld_shared_cache_some_image_overridden();
    
        for (EACH_HEADER) {
            if (! mustReadClasses(hi, hasDyldRoots)) {
                // Image is sufficiently optimized that we need not call readClass()
                continue;
            }
            //从编译后的类列表中取出所有类,即从Mach-O中获取静态段__objc_classlist,是一个classref_t类型的指针
            classref_t const *classlist = _getObjc2ClassList(hi, &count);
    
            bool headerIsBundle = hi->isBundle();
            bool headerIsPreoptimized = hi->hasPreoptimizedClasses();
    
            for (i = 0; i < count; i++) {
                //此时获取的cls只是一个地址
                Class cls = (Class)classlist[i];
                //读取类,经过这步后,cls获取的值才是一个名字
                Class newCls = readClass(cls, headerIsBundle, headerIsPreoptimized);
                //经过调试,并未执行if里面的流程
                //初始化所有懒加载的类需要的内存空间,但是懒加载类的数据现在是没有加载到的,连类都没有初始化
                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");
        //4、修复重映射一些没有被镜像文件加载进来的类
        // Fix up remapped classes
        // Class list and nonlazy class list remain unremapped.
        // Class refs and super refs are remapped for message dispatching.
        //经过调试,并未执行if里面的流程
        //将未映射的Class 和 Super Class重映射,被remap的类都是懒加载的类
        if (!noClassesRemapped()) {
            for (EACH_HEADER) {
                //Mach-O中静态段 __objc_classrefs
                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?
                //Mach_O中静态段 __objc_superrefs
                classrefs = _getObjc2SuperRefs(hi, &count);
                for (i = 0; i < count; i++) {
                    remapClassRef(&classrefs[i]);
                }
            }
        }
    
        ts.log("IMAGE TIMES: remap classes");
    
    #if SUPPORT_FIXUP
        //5、修复一些消息
        // Fix up old objc_msgSend_fixup call sites
        for (EACH_HEADER) {
            // _getObjc2MessageRefs 获取Mach-O中静态段 __objc_msgrefs
            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里面的流程
            //遍历将函数指针进行注册,并fix为新的函数指针
            for (i = 0; i < count; i++) {
                fixupMessageRef(refs+i);
            }
        }
    
        ts.log("IMAGE TIMES: fix up objc_msgSend_fixup");
    #endif
    
        bool cacheSupportsProtocolRoots = sharedCacheSupportsProtocolRoots();
        //6、当类里面有协议时:readProtocol 读取协议
        //遍历所有协议列表,并且将协议列表加载到Protocol的哈希表中
        // Discover protocols. Fix up protocol refs.
        for (EACH_HEADER) {
            extern objc_class OBJC_CLASS_$_Protocol;
            //cls = Protocol类,所有协议和对象的结构体都类似,isa都对应Protocol类
            Class cls = (Class)&OBJC_CLASS_$_Protocol;
            ASSERT(cls);
            //创建protocol哈希表 -- protocol_map
            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();
            //通过_getObjc2ProtocolList 获取到Mach-O中静态段__objc_protolist协议列表,即从编译器中读取并初始化protocol
            protocol_t * const *protolist = _getObjc2ProtocolList(hi, &count);
            for (i = 0; i < count; i++) {
                //循环遍历协议列表,通过readProtocol方法将协议添加到protocol_map哈希表中
                readProtocol(protolist[i], cls, protocol_map, 
                             isPreoptimized, isBundle);
            }
        }
    
        ts.log("IMAGE TIMES: discover protocols");
        //7、修复没有被加载的协议
        //通过 _getObjc2ProtocolRefs 获取到Mach-O中静态段 __objc_protorefs(与6中的__objc_protolist并不是同一个东西),然后遍历需要修复的协议,通过remapProtocolRef比较当前协议和协议列表中的同一个内存地址的协议是否相同,如果不同则替换
        // Fix up @protocol references
        // Preoptimized images may have the right 
        // answer already but we don't know for sure.
        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;
            protocol_t **protolist = _getObjc2ProtocolRefs(hi, &count);
            for (i = 0; i < count; i++) {
                remapProtocolRef(&protolist[i]);
            }
        }
    
        ts.log("IMAGE TIMES: fix up @protocol references");
        //8、分类处理
        //主要是处理分类,需要在分类初始化并将数据加载到类后才执行,对于运行时出现的分类,将分类的发现推迟到对_dyld_objc_notify_register的调用完成后的第一个load_images调用为止
        // 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
        if (didInitialAttachCategories) {
            for (EACH_HEADER) {
                load_categories_nolock(hi);
            }
        }
    
        ts.log("IMAGE TIMES: discover categories");
    
        // 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)
        //初始化非懒加载类,进行rw、ro等操作:realizeClassWithoutSwift
        //懒加载类 -- 别人不动我,我就不动
        //实现非懒加载的类,对于load方法和静态实例变量
        for (EACH_HEADER) {
            //通过_getObjc2NonlazyClassList获取Mach-O中静态段__objc_nlclslist非懒加载类表
            classref_t const *classlist = 
                _getObjc2NonlazyClassList(hi, &count);
            for (i = 0; i < count; i++) {
                Class cls = remapClass(classlist[i]);
                
                const char *mangledName  = cls->mangledName();
                const char *LGPersonName = "LGPerson";
               
                if (strcmp(mangledName, LGPersonName) == 0) {
                    auto kc_ro = (const class_ro_t *)cls->data();
                    printf("_getObjc2NonlazyClassList: 这个是我要研究的 %s \n",LGPersonName);
                }
                
                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
                }
                //实现当前的类,因为前面readClass读取到内存的仅仅只有地址+名称,类的data数据并没有加载出来
                //实现所有非懒加载的类(实例化类对象的一些信息,例如rw)
                realizeClassWithoutSwift(cls, nil);
            }
        }
    
        ts.log("IMAGE TIMES: realize non-lazy classes");
        //主要是实现没有被处理的类,优化被侵犯的类
        // Realize newly-resolved future classes, in case CF manipulates them
        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, nil);
                cls->setInstancesRequireRawIsaRecursively(false/*inherited*/);
            }
            free(resolvedFutureClasses);
        }
    
        ts.log("IMAGE TIMES: realize future classes");
    
        if (DebugNonFragileIvars) {
            //实现所有类
            realizeAllClasses();
        }
    
    //省略。。。。。。
    
    #undef EACH_HEADER
    }
    

    class_ro_t

    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_rw_t

    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;
    
    //省略。。。。。。
    
    public:
        void setFlags(uint32_t set)
        {
            __c11_atomic_fetch_or((_Atomic(uint32_t) *)&flags, set, __ATOMIC_RELAXED);
        }
    
        void clearFlags(uint32_t clear) 
        {
            __c11_atomic_fetch_and((_Atomic(uint32_t) *)&flags, ~clear, __ATOMIC_RELAXED);
        }
    
        // set and clear must not overlap
        void changeFlags(uint32_t set, uint32_t clear) 
        {
            ASSERT((set & clear) == 0);
    
            uint32_t oldf, newf;
            do {
                oldf = flags;
                newf = (oldf | set) & ~clear;
            } while (!OSAtomicCompareAndSwap32Barrier(oldf, newf, (volatile int32_t *)&flags));
        }
    
        class_rw_ext_t *ext() const {
            return get_ro_or_rwe().dyn_cast<class_rw_ext_t *>();
        }
    
        class_rw_ext_t *extAllocIfNeeded() {
            auto v = get_ro_or_rwe();
            //判断rwe是否存在
            if (fastpath(v.is<class_rw_ext_t *>())) {
                //如果存在,则直接获取
                return v.get<class_rw_ext_t *>();
            } else {
                //如果不存在则进行开辟
                return extAlloc(v.get<const class_ro_t *>());
            }
        }
    
        class_rw_ext_t *deepCopy(const class_ro_t *ro) {
            return extAlloc(ro, true);
        }
    
        /**
         通过源码可知ro的获取主要分两种情况:有没有运行时,
         如果有运行时,从rw中读取
         反之,如果没有运行时,从ro中读取
         */
        const class_ro_t *ro() const {
            auto v = get_ro_or_rwe();
            if (slowpath(v.is<class_rw_ext_t *>())) {
                return v.get<class_rw_ext_t *>()->ro;
            }
            return v.get<const class_ro_t *>();
        }
    
        void set_ro(const class_ro_t *ro) {
            auto v = get_ro_or_rwe();
            if (v.is<class_rw_ext_t *>()) {
                v.get<class_rw_ext_t *>()->ro = ro;
            } else {
                set_ro_or_rwe(ro);
            }
        }
    
        const method_array_t methods() const {
            auto v = get_ro_or_rwe();
            if (v.is<class_rw_ext_t *>()) {
                return v.get<class_rw_ext_t *>()->methods;
            } else {
                return method_array_t{v.get<const class_ro_t *>()->baseMethods()};
            }
        }
    
        const property_array_t properties() const {
            auto v = get_ro_or_rwe();
            if (v.is<class_rw_ext_t *>()) {
                return v.get<class_rw_ext_t *>()->properties;
            } else {
                return property_array_t{v.get<const class_ro_t *>()->baseProperties};
            }
        }
    
        const protocol_array_t protocols() const {
            auto v = get_ro_or_rwe();
            if (v.is<class_rw_ext_t *>()) {
                return v.get<class_rw_ext_t *>()->protocols;
            } else {
                return protocol_array_t{v.get<const class_ro_t *>()->baseProtocols};
            }
        }
    };
    

    class_rw_ext_t

    struct class_rw_ext_t {
        const class_ro_t *ro;
        method_array_t methods;
        property_array_t properties;
        protocol_array_t protocols;
        char *demangledName;
        uint32_t version;
    };
    

    attachLists

    attachList3种情况.png
    //连接列表,拼接列表
        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对1是一种一维赋值,函数路径为:map_images -> _read_images -> readClass -> realizeClassWithoutSwift -> methodizeClass -> prepareMethodLists -> fixupMethodList -> attachToClass -> load_categories_nolock -> attachCategories -> extAllocIfNeeded -> extAlloc -> attachLists
                // 0 lists -> 1 list
                //将list加入mlists的第一个元素,此时的list是一个一维数组
                list = addedLists[0];
            } 
            else {
                //如果本类只有一个分类,则会走到情况3,即1对多的情况
                // 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]));
            }
        }
    
    懒加载类与非懒加载类.png

    总结

    readClass主要是读取类,即此时的类仅有地址+名称,还没有data数据
    realizeClassWithoutSwift主要是实现类,即将类的data数据读取到内存中
    1.methodizeClass方法中实现类中方法(协议等)的序列化
    2.attachCategories方法中实现类以及分类的数据加载


    类的加载流程.png

    参考资料
    iOS-底层原理 17:类的加载(上)

    相关文章

      网友评论

          本文标题:类的加载

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