OC底层原理(16)- 类的加载原理(下)

92 阅读2分钟

指针强转到数据结构

realizeClassWithoutSwift方法里面的auto ro = (const class_ro_t *)cls->data();,其实就是用到了将指针强转到数据结构。

objc_class 里面

class_rw_t *data() const {
        return bits.data();
    }

class_data_bits_t 里面

class_rw_t* data() const {
        return (class_rw_t *)(bits & FAST_DATA_MASK);
    }

attachCategories反推

image.png

rwe 何时赋值的?

attachCategories 里面 auto rwe = cls->data()->extAllocIfNeeded();

哪些api调用了attachCategories?

1、attachToClass 调用了 attachCategories

1.1 attachToClass什么时候调用? methodizeClass调用了attachToClass

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

attachToClass的调用取决于previously,而 previously的值在methodizeClass(Class cls, Class previously)赋值,继续往上推导,可以找到 realizeClassWithoutSwift(Class cls, Class previously),再继续往上找,可以发现所有的realizeClassWithoutSwift(cls, nil);调用,传的previously都是 nil

因此只有在objc::unattachedCategories.attachToClass(cls, cls,isMeta ? ATTACH_METACLASS :ATTACH_CLASS);调用了。

2、load_categories_nolock 调用了 attachCategories

attachList算法

void attachLists(List* const * addedLists, uint32_t addedCount) {
    if (addedCount == 0) return;

    if (hasArray()) {
        // many lists -> many lists
        uint32_t oldCount = array()->count;
        uint32_t newCount = oldCount + addedCount;
        array_t *newArray = (array_t *)malloc(array_t::byteSize(newCount));
        newArray->count = newCount;
        array()->count = newCount;

        for (int i = oldCount - 1; i >= 0; i--)
            newArray->lists[i + addedCount] = array()->lists[i];
        for (unsigned i = 0; i < addedCount; i++)
            newArray->lists[i] = addedLists[i];
        free(array());
        setArray(newArray);
        validate();
    }
    else if (!list  &&  addedCount == 1) {
        // 0 lists -> 1 list
        list = addedLists[0];
        validate();
    } 
    else {
        // 1 list -> many lists
        Ptr<List> oldList = list;
        uint32_t oldCount = oldList ? 1 : 0;
        uint32_t newCount = oldCount + addedCount;
        setArray((array_t *)malloc(array_t::byteSize(newCount)));
        array()->count = newCount;
        if (oldList) array()->lists[addedCount] = oldList;
        for (unsigned i = 0; i < addedCount; i++)
            array()->lists[i] = addedLists[i];
        validate();
    }
}

新添加的list放到newArray的前面,旧的list放到newArray的后面。

分类和主类加载

根据 load 方法是否实现,看看类是如何加载的

1、类和分类都实现load方法

_read_images 懒加载类 - realizeClassWithoutSwift - load_categories_nolock - attachCategories

attachCategories 方法比较耗时。

2、分类实现load,类没有实现

_read_images - realizeClassWithoutSwift - methodizeClass - attachToClass ,没有走 attachCategories

通过data()获取

3、分类没有实现,类实现load方法

_read_images - realizeClassWithoutSwift - methodizeClass - attachToClass,没有走 attachCategories 通过data()获取

4、分类和类都没有实现load方法

推迟到第一次消息发送的时候 初始化

通过data()获取

5、类和分类都实现load方法,分类有多个,但部分分类没有实现load方法

小结:尽量减少load 方法的使用,否则会比较耗时。

类扩展 VS 分类

image.png

简言之,分类可以给类添加方法;扩展类给类添加私有属性和方法

关联对象

image.png

image.png

关联对象:设值流程

image.png

关联对象:取值流程

image.png