美文网首页
应用程序加载(三)-- 类的加载

应用程序加载(三)-- 类的加载

作者: 过气的程序员DZ | 来源:发表于2020-10-16 19:56 被阅读0次

    应用程序加载(一) -- dyld流程分析
    应用程序加载(二) -- dyld&objc关联以及类的加载初探
    应用程序加载(三)-- 类的加载
    应用程序加载(四)-- 分类的加载
    应用程序加载(五)-- 类扩展和关联对象


    开场白

    上篇文章研究到_read_images中调用了readClass函数。这篇继续开始我们的研究

    readClass

    Class readClass(Class cls, bool headerIsBundle, bool headerIsPreoptimized)
    {
        const char *mangledName  = cls->mangledName();
        
        //省略不关心的代码
        ...
        
        
        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 {
            addNamedClass(cls, mangledName, replacing);
            addClassTableEntry(cls);
        }
        //省略不关心的代码
        ...
        
        return cls;
    }
    
    //获取类名
    
    
    • 从类中获取类名,后续将类信息加入到哈希表中,类名作为key。获取类名的实现如图:
    • 调用函数addNamedClass(cls, mangledName, replacing);,把类和类名作为参数传入进去,实现如图:

    看看加入表的代码实现:

    void *NXMapInsert(NXMapTable *table, const void *key, const void *value) {
        MapPair *pairs = (MapPair *)table->buckets;
        unsigned    index = bucketOf(table, key);
        MapPair *pair = pairs + index;
        if (key == NX_MAPNOTAKEY) {
        _objc_inform("*** NXMapInsert: invalid key: -1\n");
        return NULL;
        }
    
        unsigned numBuckets = table->nbBucketsMinusOne + 1;
        
        //哈希表中没有 直接加入
        if (pair->key == NX_MAPNOTAKEY) {
        pair->key = key; pair->value = value;
        table->count++;
        if (table->count * 4 > numBuckets * 3) _NXMapRehash(table);
        return NULL;
        }
        
        
        if (isEqual(table, pair->key, key)) {//表中有,并且key能匹配上,直接修改原来记录的value
        const void  *old = pair->value;
        if (old != value) pair->value = value;/* avoid writing unless needed! */
        return (void *)old;
        } else if (table->count == numBuckets) {
        /* no room: rehash and retry */
        _NXMapRehash(table);
        return NXMapInsert(table, key, value);
        } else {//key匹配不上,哈希冲突,重新计算哈希并加入到表
        unsigned    index2 = index;
        while ((index2 = nextIndex(table, index2)) != index) {
            pair = pairs + index2;
            if (pair->key == NX_MAPNOTAKEY) {
            pair->key = key; pair->value = value;
            table->count++;
            if (table->count * 4 > numBuckets * 3) _NXMapRehash(table);
            return NULL;
            }
            if (isEqual(table, pair->key, key)) {
            const void  *old = pair->value;
            if (old != value) pair->value = value;/* avoid writing unless needed! */
            return (void *)old;
            }
        }
        /* no room: can't happen! */
        _objc_inform("**** NXMapInsert: bug\n");
        return NULL;
        }
    }
    
    • 代码中有涉及到两个结构体:NXMapTableMapPair

    • MapPair结构体有两个成员,分别是keyvalue

    • NXMapTable结构体中相对复杂一些,如图

    • NXMapInsert实现中,先从NXMapTable中获取buckets,并且强转成MapPair*类型

    • 获取到最大偏移量的位置,进行判断

      • 如果该位置上没有值,直接进行存储。之前获取的类名作为key,而value就是类的信息cls
      • 位置上有值,并且与传入的参数key相等,直接替换old value
      • 如果不相等,说明哈希冲突,重新计算一个位置再进行存储

    到此处,仅仅是将类名和类的总体信息保存到系统的一个表中。下面了解一下类中方法做了什么处理。

    realizeClassWithoutSwift

    _read_images中,会看到调用函数realizeClassWithoutSwift的地方

    • 注释中有一个关键字non-lazy classes,意思是:当实现了+load方法,那么这个类就是非懒加载类。
    • 相反,如果不实现+load方法,就是懒加载类。懒加载类是在方法查找时调用的。其实很好理解,既然要查找类的方法,那么首先类必须得存在。

    懒加载类和非懒加载类的函数调用栈,如图:


    接下来看看realizeClassWithoutSwift的实现,代码比较长,通过多张图片的方式查看:

    • 首先根据传入的参数cls,获取ro
    • 然后用给临时变量rw开辟空间,并且将ro赋值给rw中。
    • 最后讲rw赋值给cls
    • 既然要实现类,那么也要完善他继承关系和元类的相关信息,所以此处进行递归
    • 后续代码就是对元类non-pointer isa等相关的处理了,这部分代码就不贴了,感兴趣可自行查看
    • 最后调用了methodizeClass函数。注意:由于递归的原因,此处的cls可能是父类或者元类。

    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();
        
        const char *mangledName  = cls->mangledName();
    
       //省略代码
       ...
    
        // Install methods and properties that the class implements itself.
        method_list_t *list = ro->baseMethods();
        if (list) {
            prepareMethodLists(cls, &list, 1, YES, isBundleClass(cls));
            if (rwe) rwe->methods.attachLists(&list, 1);
        }
    
        property_list_t *proplist = ro->baseProperties;
        if (rwe && proplist) {
            rwe->properties.attachLists(&proplist, 1);
        }
    
        protocol_list_t *protolist = ro->baseProtocols;
        if (rwe && protolist) {
            rwe->protocols.attachLists(&protolist, 1);
        }
    
       //省略代码
       ...
    }
    
    • 通过源码能够看到,分别处理了方法、属性、协议
    • 处理方法的时候,调用了prepareMethodLists函数
    static void 
    prepareMethodLists(Class cls, method_list_t **addedLists, int addedCount,
                       bool baseMethods, bool methodsFromBundle)
    {
        runtimeLock.assertLocked();
        
        const char *mangledName  = cls->mangledName();
    
        if (addedCount == 0) return;
    
        //省略代码
       ...
    
        // Add method lists to array.
        // Reallocate un-fixed method lists.
        // The new methods are PREPENDED to the method list array.
    
        for (int i = 0; i < addedCount; i++) {
            method_list_t *mlist = addedLists[i];
            ASSERT(mlist);
    
            // Fixup selectors if necessary
            if (!mlist->isFixedUp()) {
                fixupMethodList(mlist, methodsFromBundle, true/*sort*/);
            }
        }
    
       //省略代码
       ...
    }
    
    static void 
    fixupMethodList(method_list_t *mlist, bool bundleCopy, bool sort)
    {
        runtimeLock.assertLocked();
        ASSERT(!mlist->isFixedUp());
    
        // fixme lock less in attachMethodLists ?
        // dyld3 may have already uniqued, but not sorted, the list
        if (!mlist->isUniqued()) {
            mutex_locker_t lock(selLock);
        
            // Unique selectors in list.
            for (auto& meth : *mlist) {
                const char *name = sel_cname(meth.name);
                meth.name = sel_registerNameNoLock(name, bundleCopy);
            }
        }
    
        // Sort by selector address.
        if (sort) {
            method_t::SortBySELAddress sorter;
            std::stable_sort(mlist->begin(), mlist->end(), sorter);
        }
        
        // Mark method list as uniqued and sorted
        mlist->setFixedUp();
    }
    
    • methodizeClass中调用了fixupMethodList
    • fixupMethodList实现中对方法的sel进行了一个排序处理。在之前的文章方法调用(二)-- 慢速查找流程中,方法查找时使用了二分查找。而二分查找必须是有序的。这也是此处进行排序的原因。
    • 注意注释Sort by selector address.,是按照sel的地址进行排序的。

    类加载流程图

    相关文章

      网友评论

          本文标题:应用程序加载(三)-- 类的加载

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