iOS-类&分类的加载

realizeClassWithoutSwift

realizeClassWithoutSwift是初始化类的核心方法,主要做了如下事情:

  1. 读取data数据,设置ro、rw
  2. 归调用realizeClassWithoutSwift完善类的继承链关系
  3. 调用methodizeClass,完善类信息(方法、分类的方法、属性列表、协议列表)

读取 data 数据,并设置 ro 、rw
- ro 表示 read only,其在编译期的时候就已经确定了内存,包含了类的名称、方法列表、协议列表、属性列表和成员变量列表的信息。由于它是只读的,确定了之后就不会发生变化,所以属于 干净的内存(Clean Memory)
- rw 表示 read Write,由于 OC 的动态性,所以可能会在运行时动态往类中添加属性、方法和协议
- rwe 表示 read Write ext,是对rw做了进一步的改进。 rw 中只有 10% 左右的类真正更改了它们的方法、属性等,所以新增加了 rwe,即是类的额外信息,rw 和 rwe 都属于 脏内存(dirty memory)

auto ro = (const class_ro_t *)cls->data();
// 判断是否是元类
auto isMeta = ro->flags & RO_META;
if (ro->flags & RO_FUTURE) {
    // This was a future class. rw data is already allocated.
    rw = cls->data();
    ro = cls->data()->ro();
    ASSERT(!isMeta);
    cls->changeInfo(RW_REALIZED|RW_REALIZING, RW_FUTURE);
} else {
    // Normal class. Allocate writeable class data.
    // 创建 rw
    rw = objc::zalloc<class_rw_t>();
    // ro 赋值到 rw
    rw->set_ro(ro);
    rw->flags = RW_REALIZED|RW_REALIZING|isMeta;
    // 将 rw 赋值给 cls 的 data
    cls->setData(rw);
}

递归调用 realizeClassWithoutSwift 完成类的继承链关系

// 递归调用 `realizeClassWithoutSwift`,设置父类和元类的 `data` 信息加载到内存当中。
// 当 isa 找到根元类之后,因为根元类的 isa 是指向自己的,不会返回 nil 从而导致无限递归,在 remapClass 中对类在表中进行查找的操作,如果表中已有该类,则返回一个空值;如果没有则返回当前类,这样保证了类只加载一次并结束递归
supercls = realizeClassWithoutSwift(remapClass(cls->superclass), nil);
metacls = realizeClassWithoutSwift(remapClass(cls->ISA()), nil);

// Update superclass and metaclass in case of remapping
// 分别将父类和元类赋值给 `class` 的 `superclass` 和 `classIsa`。
cls->superclass = supercls;
cls->initClassIsa(metacls);

// Connect this class to its superclass's subclass lists
// 注意:class 是双向链接结构,即父类可以找到子类,子类可以找到父类
if (supercls) {
    // 将 cls 作为 supercls 的子类添加。
    addSubclass(supercls, cls);
} else {
    // 将 cls 添加为新的已实现的根类。
    addRootClass(cls);
}

调用 methodizeClass 方法,读取方法列表(包括分类)、协议列表、属性列表,然后赋值给 rw,最后返回 cls

// Attach categories - 附加分类 
methodizeClass(cls, previously);

return cls;

methodizeClass

methodizeClass主要做了两件事:

  1. 将方法列表、属性列表和协议列表赋值给 rwe。
  2. 附加分类方法。
/***********************************************************************
* 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();
    
    // Install methods and properties that the class implements itself.
    // 将方法列表、属性列表和协议列表赋值到 rwe
    // 获取 ro 的 方法列表
    method_list_t *list = ro->baseMethods();
    if (list) {
        // 对方法列表进行排序操作
        prepareMethodLists(cls, &list, 1, YES, isBundleClass(cls));
        // 把方法赋值给 rwe 的 method
        if (rwe) rwe->methods.attachLists(&list, 1);
    }
    
    // 获取 ro 的 属性列表
    property_list_t *proplist = ro->baseProperties;
    if (rwe && proplist) {
        // 把属性赋值给 rwe 的 properties
        rwe->properties.attachLists(&proplist, 1);
    }
    
    // 获取 ro 的 协议列表
    protocol_list_t *protolist = ro->baseProtocols;
    if (rwe && protolist) {
        // 把协议赋值给 rwe 的 protocols
        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);
}

attachToClass

把分类的数据加入到主类数据中

void attachToClass(Class cls, Class previously, int flags)
{
    runtimeLock.assertLocked();
    ASSERT((flags & ATTACH_CLASS) ||
           (flags & ATTACH_METACLASS) ||
           (flags & ATTACH_CLASS_AND_METACLASS));
           
    auto &map = get();
    // 找到一个分类就进来一次,防止混乱
    auto it = map.find(previously);
    
    // 注意:当主类没有实现 load 方法的时候,分类实现了 load 方法,那么会迫使主类进行加载,所以会进入 if 判断里面
    if (it != map.end()) {
        category_list &list = it->second;
        // 判断是否元类
        if (flags & ATTACH_CLASS_AND_METACLASS) {
            int otherFlags = flags & ~ATTACH_CLASS_AND_METACLASS;
            // 附加实例方法
            attachCategories(cls, list.array(), list.count(), otherFlags | ATTACH_CLASS);
            // 附加类方法
            attachCategories(cls->ISA(), list.array(), list.count(), otherFlags | ATTACH_METACLASS);
        } else {
            // 附加实例方法
            attachCategories(cls, list.array(), list.count(), flags);
        }
        map.erase(it);
    }
}

// Attach method lists and properties and protocols from categories to a class.
// Assumes the categories in cats are all loaded and sorted by load order, 
// oldest categories first.
static void
attachCategories(Class cls, const locstamped_category_t *cats_list, uint32_t cats_count,
                 int flags)
{
    if (slowpath(PrintReplacedMethods)) {
        printReplacements(cls, cats_list, cats_count);
    }
    if (slowpath(PrintConnecting)) {
        _objc_inform("CLASS: attaching %d categories to%s class '%s'%s",
                     cats_count, (flags & ATTACH_EXISTING) ? " existing" : "",
                     cls->nameForLogging(), (flags & ATTACH_METACLASS) ? " (meta)" : "");
    }
    
    /*
     * Only a few classes have more than 64 categories during launch.
     * This uses a little stack, and avoids malloc.
     *
     * Categories must be added in the proper order, which is back
     * to front. To do that with the chunking, we iterate cats_list
     * from front to back, build up the local buffers backwards,
     * and call attachLists on the chunks. attachLists prepends the
     * lists, so the final result is in the expected order.
     */
    constexpr uint32_t ATTACH_BUFSIZ = 64;
    method_list_t   *mlists[ATTACH_BUFSIZ];
    property_list_t *proplists[ATTACH_BUFSIZ];
    protocol_list_t *protolists[ATTACH_BUFSIZ];

    uint32_t mcount = 0;
    uint32_t propcount = 0;
    uint32_t protocount = 0;
    bool fromBundle = NO;
    bool isMeta = (flags & ATTACH_METACLASS);
    // 初始化 rwe,因为要往主类添加方法、属性和协议
    auto rwe = cls->data()->extAllocIfNeeded();
    
    // mlists -> 二维数组
    for (uint32_t i = 0; i < cats_count; i++) {
        auto& entry = cats_list[i];

        method_list_t *mlist = entry.cat->methodsForMeta(isMeta);
        if (mlist) {
            // 当前 mcount = 0,ATTACH_BUFSIZ= 64,所以不会进入 if 里面
            if (mcount == ATTACH_BUFSIZ) {
                prepareMethodLists(cls, mlists, mcount, NO, fromBundle);
                rwe->methods.attachLists(mlists, mcount);
                mcount = 0;
            }
            // 倒序插入
            mlists[ATTACH_BUFSIZ - ++mcount] = mlist;
            fromBundle |= entry.hi->isBundle();
        }

        property_list_t *proplist =
            entry.cat->propertiesForMeta(isMeta, entry.hi);
        if (proplist) {
            if (propcount == ATTACH_BUFSIZ) {
                rwe->properties.attachLists(proplists, propcount);
                propcount = 0;
            }
            proplists[ATTACH_BUFSIZ - ++propcount] = proplist;
        }

        protocol_list_t *protolist = entry.cat->protocolsForMeta(isMeta);
        if (protolist) {
            if (protocount == ATTACH_BUFSIZ) {
                rwe->protocols.attachLists(protolists, protocount);
                protocount = 0;
            }
            protolists[ATTACH_BUFSIZ - ++protocount] = protolist;
        }
    }
    
    if (mcount > 0) {
        // 排序
        prepareMethodLists(cls, mlists + ATTACH_BUFSIZ - mcount, mcount, NO, fromBundle);
        // 进行内存平移操作
        rwe->methods.attachLists(mlists + ATTACH_BUFSIZ - mcount, mcount);
        if (flags & ATTACH_EXISTING) flushCaches(cls);
    }

    rwe->properties.attachLists(proplists + ATTACH_BUFSIZ - propcount, propcount);

    rwe->protocols.attachLists(protolists + ATTACH_BUFSIZ - protocount, protocount);
}

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 加入 mlists 的第一个元素,此时的 list 是一个一维数组
        list = addedLists[0]; 
    } 
    else {
        // 新的 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是否存在,如果存在,就将旧的 list 放入到数组的末尾
        if (oldList) array()->lists[addedCount] = oldList;
        // memcpy(起始位置,放什么,放多大) 内存平移,从数组起始位置存入新的list
        //其中 array()->lists 表示首位元素位置
        memcpy(array()->lists, addedLists, 
               addedCount * sizeof(array()->lists[0]));
    }
}

从上可知,插入表可分为以下三种情况:

  1. 多对多: 如果当前调用 attachLists 的 list_array_tt 二维数组中有多个一维数组。
    1.1 先计算数组中旧 list 的大小
    1.2 计算新的容量大小 = 旧数据大小 + 新数据大小
    1.3 根据新的容量大小,开辟一个数组,类型是 array_t,通过 array() 获取
    1.4 设置数组大小
    1.5 旧的数据从 addedCount 数组下标开始 存放旧的 lists,大小 = 旧数据大小 * 单个旧list大小,即指针偏移
    1.6 新数据从数组首位置开始存储,存放新的 lists,大小 = 新数据大小 * 单个list大小,可以理解越晚加进来的越在前面,越在前面,调用时则优先调用
  2. 零对一: 如果当前调用 attachLists 的 list_array_tt 二维数组为空且新增大小为1。
    2.1 直接赋值给 addedList 的第一个 list
  3. 一对多: 如果当前调用 attachLists 的 list_array_tt 二维数组只有一个一维数组。
    3.1 获取旧的list
    3.2 计算容量和 = 旧list个数 + 新lists的个数
    3.3 开辟一个容量和大小的集合,类型是 array_t,即创建一个数组,放到array 中,通过 array() 获取
    3.4 设置数组的大小
    3.5 判断 old 是否存在,如果 old 是存在的,那么就将旧的list放入到数组的末尾
    3.6 memcpy(起始位置,放什么,放多大) 内存平移,从数组起始位置存入新的list

category

可以看到,category 也是一个结构体,主要包含了category名称,类对象指针,实例方法列表,类方法列表,协议列表,属性列表等信息。但是无法添加实例变量(可以通过关联对象的方式来实现)

struct category_t {
    const char *name;
    classref_t cls;
    WrappedPtr<method_list_t, PtrauthStrip> instanceMethods;
    WrappedPtr<method_list_t, PtrauthStrip> classMethods;
    struct protocol_list_t *protocols;
    struct property_list_t *instanceProperties;
    // Fields below this point are not always present on disk.
    struct property_list_t *_classProperties;

    method_list_t *methodsForMeta(bool isMeta) {
        if (isMeta) return classMethods;
        else return instanceMethods;
    }

    property_list_t *propertiesForMeta(bool isMeta, struct header_info *hi);
    
    protocol_list_t *protocolsForMeta(bool isMeta) {
        if (isMeta) return nullptr;
        else return protocols;
    }
};

category的加载时机

分类数据的加载时机: 根据 类和分类是否实现 load 方法 来区分不同的时机。

  • 在 attachCategories 方法中准备分类数据
  • 在 attachLists 中将分类数据添加到主类

类和分类是否实现 load 方法可以分为四种情况:

类和分类分类实现 load分类未实现 load
类实现 load非懒加载类 + 非懒加载分类非懒加载类 + 懒加载分类
类未实现 load懒加载类 + 非懒加载分类懒加载类 + 懒加载分类

不同情况的加载时机:

  1. 非懒加载类 + 非懒加载分类,其数据的加载在 load_images 方法中,首先对类进行加载,然后把分类的信息贴到类中。
  2. 非懒加载类 + 懒加载分类,其数据加载在 read_image 就加载数据,数据来自data,data在编译时期就已经完成,即data中除了类的数据,还有分类的数据,与类绑定在一起。
  3. 懒加载类 + 懒加载分类,其数据加载推迟到 第一次消息时,数据同样来自data,data在编译时期就已经完成。
  4. 懒加载类 + 非懒加载分类,只要分类实现了load,会迫使主类提前加载,即在 _read_images 中不会对类做实现操作,需要在 load_images 方法中触发类的数据加载,即rwe初始化,同时加载分类数据。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值