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

应用程序加载(一) -- 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的地址进行排序的。

类加载流程图

你可能感兴趣的:(应用程序加载(三)-- 类的加载)