阿里、字节:一套高效的iOS面试题(一 - runtime 结构模型 - 中)

本文完整版共三篇:

2 类是如何加载的

...

2.2 _dyld_objc_register_callbacks

...

map_images

...

_read_images

...

第三部分:读取类
///+ 读取类
for (EACH_HEADER) {
    ///+ 镜像已被彻底优化,无需读取类
    if (! mustReadClasses(hi, hasDyldRoots)) {
        // Image is sufficiently optimized that we need not call readClass()
        continue;
    }

    ///+ 从 “__objc_classlist” 区中读取类引用
    classref_t const *classlist = _getObjc2ClassList(hi, &count);

    bool headerIsBundle = hi->isBundle();
    bool headerIsPreoptimized = hi->hasPreoptimizedClasses();  

    for (i = 0; i < count; i++) {
        ///+ 获取类地址
        Class cls = (Class)classlist[i];
        ///+ 读取该类
        Class newCls = readClass(cls, headerIsBundle, headerIsPreoptimized);  

        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.
            ///+ 类被移动了,但并未删除,仅发生一个信息解析 future 类时。
            ///+ 将该类加入到延后识别数组中
            resolvedFutureClasses = (Class *)
                realloc(resolvedFutureClasses,
                        (resolvedFutureClassCount+1) * sizeof(Class));
            resolvedFutureClasses[resolvedFutureClassCount++] = newCls;
        }
    }
}
复制代码

重点就在于 readClass 这个函数了,瞄一眼:

///+ 读取 类与元类
///+ 返回值有一下三种情况
///+    1. clas
///+    2. nil (该类有一个缺失的弱链接的父类)
///+    3. 其他 (这块空间预留给未来的类,例如前向引用)
Class readClass(Class cls, **bool** headerIsBundle, **bool** headerIsPreoptimized)
{
    ///+ 获取类名
    const char *mangledName = cls->nonlazyMangledName();

    if (missingWeakSuperclass(cls)) {
        ///+ 若该类没有父类(有可能是基类,也可能是该类在继承链上缺失父类)
        // No superclass (probably weak-linked).
        // Disavow any knowledge of this subclass.

        ///+ 添加到重映射类列表中
        addRemappedClass(cls, nil);
        cls->setSuperclass(nil);
        return nil;
    }
    
    Class replacing = nil;
    if (mangledName != nullptr) {
        ///+ 这部分看起来非常像类读取的过程,但在调试过程中发现并没有走到这里。所以忽略
        ...
    }
    
    if (headerIsPreoptimized  &&  !replacing) {
        // class list built in shared cache
        ///+ 类列表在共享缓存中构建
    } else {
        if (mangledName) { //some Swift generic classes can lazily generate their names
            ///+ 将 cls 添加到总表 gdb_objc_realized_classes 中
            ///+ 若存在类名冲突,则添加至 nomete_class_map
            ///+ <name: cls>
            ///+ 内部实现:`NXMapInsert(gdb_objc_realized_classes, name, cls);`
            addNamedClass(cls, mangledName, replacing);
        } else {
            ...
        }
        ///+ 将该类添加到在 runtime_init 就已开辟的表中
        ///+ 内部实现:objc::allocatedClasses.get().insert(cls)
        ///+ 内部实现还会调用(第二个参数默认为 true) addClassTableEntry(cls->ISA(), false)
        addClassTableEntry(cls);
    }
    
    // for future reference: shared cache never contains MH_BUNDLEs
    ///+ 如果这个 mach_header 是一个 bundle,给该类与元类打上标记
    ///+ 共享缓存绝不包含 MH_BUNDLE
    if (headerIsBundle) {
        const_cast<class_ro_t *>(cls->safe_ro())->flags |= RO_FROM_BUNDLE;
        const_cast<class_ro_t *>(cls->ISA()->safe_ro())->flags |= RO_FROM_BUNDLE;
    }

    return cls;
}
复制代码

所以 addClass 主要就是讲 cls 添加到 runtime_init 中申请的表中。

那么,什么是 weak-linked class 呢?先看一下它是如何判断的:

missingWeakSuperclass(Class cls)
{
    if (!cls->getSuperclass()) {
        // superclass nil. This is normal for root classes only.
        ///+ 没有父类,且不是根类
        return (!(cls->safe_ro()->flags & RO_ROOT));
    } else {
        // superclass not nil. Check if a higher superclass is missing.
        ///+ 父类存在,但在继承链更高的环节上没有父类,且不是根类
        Class supercls = remapClass(cls->getSuperclass());
        if (!supercls) return YES;
        if (supercls->isRealized()) return NO;
        return missingWeakSuperclass(supercls);
    }
}
复制代码

很明显,在继承链上没有父类且不是根类的类则为 weak-linked class。我粘贴了一段概念,大家可以去 原文 Frameworks and Weak Linking查看

One challenge faced by developers is that of taking advantage of new features introduced in new versions of OS X while still supporting older versions of the system. Normally, if an application uses a new feature in a framework, it is unable to run on earlier versions of the framework that do not support that feature. Such applications would either fail to launch or crash when an attempt to use the feature was made. Apple has solved this problem by adding support for weakly-linked symbols.

开发者在使用新版本系统中的新特性时仍需要棉铃兼容旧版本的挑战。通常情况下,app 在使用一个 framework 的新特性时,这是无法运行在不支持这些特性的老版本中的。当执行到这些新特性代码时,app 可能崩溃,或者是启动失败。Apple 通过添加支持弱引用符号来解决这个问题。

When a symbol in a framework is defined as weakly linked, the symbol does not have to be present at runtime for a process to continue running. The static linker identifies a weakly linked symbol as such in any code module that references the symbol. The dynamic linker uses this same information at runtime to determine whether a process can continue running. If a weakly linked symbol is not present in the framework, the code module can continue to run as long as it does not reference the symbol. However, if the symbol is present, the code can use it normally. 当 framework 中的一个符号作为弱引用时,为了正常允许该符号在运行时不会表现出来。静态链接器在任何引用该符号的代码标识弱引用符号。动态链接器在运行时使用相同的信息来决定是否可以正常运行下去。如果弱引用符号不存在,只要代码中没有引用到该符号,程序可以正常运行下去。只要该符号存在,代码就可以正常运行。

If you are updating your own frameworks, you should consider making new symbols weakly linked. Doing so can make it easier for clients of your framework to support it. You should also make sure that your own code checks for the existence of weakly-linked symbols before using them.

开发者在更新自己的 framework 时,应该考虑创建新的弱引用符号。这样做让使用使用者跟容易使用。在使用之前应该检查代码中对于弱引用符号是否存在。

第四部分:重映射类
///+ 修复/重映射类
if (!noClassesRemapped()) {
    for (EACH_HEADER) {
        ///+ 从 “__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?
        ///+ 从 “__objc_superrefs” 获取类并重映射
        classrefs = _getObjc2SuperRefs(hi, &count);
        for (i = 0; i < count; i++) {
            remapClassRef(&classrefs[i]);
        }
    }
}
复制代码

继续看

static void remapClassRef(Class *clsref)
{
    lockdebug::assert_locked(&runtimeLock);
    Class newcls = remapClass(*clsref);

    ///+ 修复/重映射这个类,与 sel 的修复相同
    if (*clsref != newcls) *clsref = newcls;
}


static Class remapClass(Class cls)
{
    lockdebug::assert_locked(&runtimeLock);

    if (!cls) return nil;

    ///+ 对于已识别的前向引用,返回 <oldCls: newCls> 的表
    ///+ 对于已忽略的弱链接的类,返回 nil
    auto *map = remappedClasses(NO);
    if (!map)
        return cls;
        
    ///+ 
    auto iterator = map->find(cls);
    if (iterator == map->end())
        ///+ 在 map 中未找到,直接返回 cls
        return cls;
    ///+ 若找到了,返回 <oldCls: newCls> 中的 newCls
    return std::get<1>(*iterator);
}
复制代码
第五部分:修复 msg
for (EACH_HEADER) {
    ///+ 从 “__**objc_msgrefs” 区中获取 msg 引用**
    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 (i = 0; i < count; i++) {
        fixupMessageRef(refs+i);
    }
}
复制代码

再看一下是如何修复 msg 的:

static void
fixupMessageRef(message_ref_t *msg)
{
    ///+ 首先注册这些方法
    msg->sel = sel_registerName((const char *)msg->sel);  

    if (msg->imp == &objc_msgSend_fixup) {
        if (msg->sel == @selector(alloc)) {
            msg->imp = (IMP)&objc_alloc;
        } else if (msg->sel == @selector(allocWithZone:)) {
            msg->imp = (IMP)&objc_allocWithZone;
        } else if (msg->sel == @selector(retain)) {
            msg->imp = (IMP)&objc_retain;
        } else if (msg->sel == @selector(release)) {
            msg->imp = (IMP)&objc_release;
        } else if (msg->sel == @selector(autorelease)) {
            msg->imp = (IMP)&objc_autorelease;
        } else {
            msg->imp = &objc_msgSend_fixedup;
        }
    }
    else if (msg->imp == &objc_msgSendSuper2_fixup) {
        msg->imp = &objc_msgSendSuper2_fixedup;
    }
    else if (msg->imp == &objc_msgSend_stret_fixup) {
        msg->imp = &objc_msgSend_stret_fixedup;
    }
    else if (msg->imp == &objc_msgSendSuper2_stret_fixup) {
        msg->imp = &objc_msgSendSuper2_stret_fixedup;
    }
#if defined(__i386__)  ||  defined(__x86_64__)
    else if (msg->imp == &objc_msgSend_fpret_fixup) {
        msg->imp = &objc_msgSend_fpret_fixedup;
    }
#endif
#if defined(__x86_64__)
    else if (msg->imp == &objc_msgSend_fp2ret_fixup) {
        msg->imp = &objc_msgSend_fp2ret_fixedup;
    }
#endid
}
复制代码

总结一下:

original imp sel repaired imp platform
objc_msgSend_fixup alloc objc_alloc All
allocWithZone: objc_allocWithZone: All
retain objc_retain All
release objc_release All
autorelease objc_autorelease All
otherwise objc_msgSend_fixedup All
objc_msgSendSuper2_fixup objc_msgSendSuper2_fixedup All
objc_msgSend_stret_fixup objc_msgSend_stret_fixedup All
objc_msgSendSuper2_stret_fixup objc_msgSendSuper2_stret_fixedup All
objc_msgSend_fpret_fixup objc_msgSend_fpret_fixedup i386, x86_64
objc_msgSend_fp2ret_fixedup objc_msgSend_fp2ret_fixedup x86_64
第六部分:读取 protocol
///+ 读取 protocol
for (EACH_HEADER) {
    extern objc_class OBJC_CLASS_$_Protocol;
    Class cls = (Class)&OBJC_CLASS_$_Protocol;
    ASSERT(cls);
    ///+ 初始化 protocol 哈希表(仅一次)
    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
    ///+ 如果当前镜像来此共享缓存并且我们支持 root,则跳过协议读取流程
    ///+ Note:在启动之后,我们需要遍历这些协议,因为共享缓存中的协议被 isCanonial() 标记,
    ///+ 但如果一些非共享缓存二进制作为规范定义是,这可能是不可信的。
    if (launchTime && isPreoptimized) {
        if (PrintProtocols) {
            _objc_inform("PROTOCOLS: Skipping reading protocols in image: %s",
                         hi->fname());
        }
        continue;
    }  

    ///+ 该镜像是否是一个 bundle
    bool isBundle = hi->isBundle();  

    ///+ 从 “__**objc_protolist” 区读取 protocol**
    protocol_t * const *protolist = _getObjc2ProtocolList(hi, &count);
    for (i = 0; i < count; i++) {
        ///+ 读取协议
        readProtocol(protolist[i], cls, protocol_map,
                     isPreoptimized, isBundle);
    }
}
复制代码

很明显,重点在 readProtocol 中,来看一下:

static void
readProtocol(protocol_t *newproto, Class protocol_class,
             NXMapTable *protocol_map,
             bool headerIsPreoptimized, bool headerIsBundle)
{
    // This is not enough to make protocols in unloaded bundles safe,
    // but it does prevent crashes when looking up unrelated protocols.
    ///+ 这不能够保证在未加载的 bundle 中创建协议时的安全
    ///+ 但足以防止查找不关联的 protocol 时可能的崩溃
    auto insertFn = headerIsBundle ? NXMapKeyCopyingInsert : NXMapInsert;  

    ///+ 通过协议名从哈希表中获取协议
    protocol_t *oldproto = (protocol_t *)getProtocol(newproto->mangledName);  

    ///+ 在哈希表中找到了协议
    if (oldproto) {
        if (oldproto != newproto) {
            // Some other definition already won.
            ///+ 另外的定义被确认为该 protocol

            // If we are a shared cache binary then we have a definition of this
            // protocol, but if another one was chosen then we need to clear our
            // isCanonical bit so that no-one trusts it.
            // Note, if getProtocol returned a shared cache protocol then the
            // canonical definition is already in the shared cache and we don't
            // need to do anything.
            ///+ 如果在共享缓存二进制文件中,存在这个协议的定义,但另一个地方的时候被选择了
            ///+ 我们需要清理 isCanonical,因此不可以信任它
            ///+ Note:如果 getProtocol 返回了一个来自共享缓存中的协议,这意味着这个规范已经定义为共享缓存中
            ///+ Note:因此不需要做任何事情
            if (headerIsPreoptimized && !oldproto->isCanonical()) {
                // Note newproto is an entry in our __objc_protolist section which
                // for shared cache binaries points to the original protocol in
                // that binary, not the shared cache uniqued one.
                ///+ newproto 是 “__**objc_protolist” 区中的一个入口,**
                ///+ 对于共享缓存二进制文件来说,它指向该二进制文件中原始地址,而不是共享缓存中的唯一缓存
                auto cacheproto = (protocol_t *)
                    getSharedCachePreoptimizedProtocol(newproto->mangledName);
                if (cacheproto && cacheproto->isCanonical())
                    cacheproto->clearIsCanonical();
            }
        }
    }
    ///+ 协议表中没有找到协议 且 该镜像被预优化了
    else if (headerIsPreoptimized) {
        // Shared cache initialized the protocol object itself,
        // but in order to allow out-of-cache replacement we need
        // to add it to the protocol table now.  

        protocol_t *cacheproto = (protocol_t *)
            getPreoptimizedProtocol(newproto->mangledName);
        protocol_t *installedproto;
        ///+ 确定真实的 protocol
        if (cacheproto  &&  cacheproto != newproto) {
            // Another definition in the shared cache wins (because
            // everything in the cache was fixed up to point to it).
            ///+ 确认在共享缓存中的另一个定义为该 protocol
            ///+ (因为缓存中的任何东西都已经被修复为指向其真实定义)
            installedproto = cacheproto;
        }
        else {
            // This definition wins.
            ///+ 确认当前这个定义为该 protocol
            installedproto = newproto;
        }  

        ASSERT(installedproto->getIsa() == protocol_class);
        ASSERT(installedproto->size >= sizeof(protocol_t));
        ///+ 已 <name: protocol> 的格式将该协议存至协议表中
        insertFn(protocol_map, installedproto->mangledName,
                 installedproto);
    }
    ///+ 协议表中没有找到协议 且 该镜像未被预优化
    else {
        // New protocol from an un-preoptimized image. Fix it up in place.
        // fixme duplicate protocols from unloadable bundle
        ///+ 从未预优化的镜像中获取的协议。初始化后将其放入协议哈希表中
        ///+ (但是这里可能会 无法加载 bundle 存在重复协议的情况)
        newproto->initIsa(protocol_class);  // fixme pinned
        insertFn(protocol_map, newproto->mangledName, newproto);
    }
}
复制代码

总结下来,找到真正的 protocol 定义并添加到哈希表中。

第七部分:重映射协议
///+ 重映射协议
///+ 预优化镜像中的协议指向可能是正确的,但不确定
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 && hi->isPreoptimized())
        continue;
    protocol_t **protolist = _getObjc2ProtocolRefs(hi, &count);
    for (i = 0; i < count; i++) {
        ///+ 按照 {isCanonial() > 哈希表中的协议 > 预优化过的协议 > 匹配的 swift 协议} 优先级进行重新映射操作
        remapProtocolRef(&protolist[i]);
    }
}
复制代码

除了在启动时跳过预优化镜像中的引用之外,其余协议按照 {isCanonial() > 哈希表中的协议 > 预优化过的协议 > 匹配的 swift 协议} 优先级进行重新映射操作

第八部分:加载分类
// 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
///+ 加载分类
///+ 仅在初始分类附加操作完成时候执行。
///+ 对于启动时就出现的分类,加载操作将延迟到第一次 _dyld_objc_notify_register 中注册的 load_images 调用之后进行
if (didInitialAttachCategories) {
    for (EACH_HEADER) {
        load_categories_nolock(hi);
    }
}
复制代码

很明显,重点在 load_categories_nolock,但仅在 load_images 调用之后执行,因为 didInitialAttachCategoriesload_iamges 中才会置为 true。

static void load_categories_nolock(header_info *hi) {
    ///+ 分类中是否包含类属性
    bool hasClassProperties = hi->info()->hasCategoryClassProperties();  

    size_t count;
    ///+ 定义一个函数指针
    auto processCatlist = [&](category_t * const *catlist) {
        for (unsigned i = 0; i < count; i++) {
            category_t *cat = catlist[i];
            ///+ 获取真实的类
            Class cls = remapClass(cat->cls);
            locstamped_category_t lc{cat, hi};  

            ///+ 类的目标类缺失,跳过
            if (!cls) {
                continue;
            }  

            // Process this category.
            if (cls->isStubClass()) {
                // Stub classes are never realized. Stub classes
                // don't know their metaclass until they're
                // initialized, so we have to add categories with
                // class methods or properties to the stub itself.
                // methodizeClass() will find them and add them to
                // the metaclass as appropriate.
                ///+ stub 类永远不会被实现。
                ///+ stub 类知道被初始化才会知道其元类,因此将分类中的类方法与类属性添加到它们自身。
                ///+ methodizeClass() 将会寻找这些类方法与类属性并添加到元类中。
                if (cat->instanceMethods ||
                    cat->protocols ||
                    cat->instanceProperties ||
                    cat->classMethods ||
                    cat->protocols ||
                    (hasClassProperties && cat->_classProperties))
                {
                    ///+ 将该协议添加到未解析的协议列表中
                    ///+ 存储格式为 <cls: [(category, hi), ...]>
                    objc::unattachedCategories.addForClass(lc, cls);
                }
            } else {
                // First, register the category with its target class.
                // Then, rebuild the class's method lists (etc) if
                // the class is realized.
                ///+ 首先,将分类注册到目标类
                ///+ 然后,如果类已经被实现了,重新构建类的方法列表
                
                ///+ 将协议中的实例方法,实例属性附加到类中
                if (cat->instanceMethods ||  cat->protocols
                    ||  cat->instanceProperties)
                {
                    if (cls->isRealized()) {
                        ///+ 将分类附加到分类上
                        attachCategories(cls, &lc, 1, ATTACH_EXISTING);
                    } else {
                        ///+ 将该协议添加到未解析的协议列表中
                        ///+ 存储格式为 <cls: [(category, hi), ...]>
                        objc::unattachedCategories.addForClass(lc, cls);
                    }
                }  

                ///+ 将协议中的类方法,类属性附加到元类中
                if (cat->classMethods  ||  cat->protocols
                    ||  (hasClassProperties && cat->_classProperties))
                {
                    if (cls->ISA()->isRealized()) {
                        attachCategories(cls->ISA(), &lc, 1, ATTACH_EXISTING | ATTACH_METACLASS);
                    } else {
                        ///+ 将该协议添加到未解析的协议列表中
                        ///+ 存储格式为 <cls: [(category, hi), ...]>
                        objc::unattachedCategories.addForClass(lc, cls->ISA());
                    }
                }
            }
        }
    };  

    ///+ 调用上边定义的函数指针,添加该镜像中的分类
    processCatlist(hi->catlist(&count));
    processCatlist(hi->catlist2(&count));
}
复制代码

总结起来:load_categories_nolock,遍历镜像中的分类列表:

  1. 逐个将分类中的类方法,类属性都添加到目标类的元类中
  2. 将实例方法,实例属性添加目标类中
  3. 如果目标类为 stub 类或还未实现,则先将该分类按照 <cls: [(category, hi), ...]> 的格式添加到未解析协议哈希表中。

然后看一下 attachCategories

static void
attachCategories(Class cls, const locstamped_category_t *cats_list, uint32_t cats_count, int flags)
{
    /*
     * 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.
     */
    ///+ 在启动期间,只有极少部分类存在超过 64 个分类。使用较小的空间来避免空间浪费
    ///+
    ///+ 分类必须按照其实际顺序(在 compile list 中的顺序)从后往前添加。
    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);
    ///+ 获取类的额外存储空间
    auto rwe = cls->data()->extAllocIfNeeded();
 

    for (uint32_t i = 0; i < cats_count; i++) {
        auto& entry = cats_list[i];  

        ///+ 获取方法列表
        method_list_t *mlist = entry.cat->methodsForMeta(isMeta);
        if (mlist) {
            if (mcount == ATTACH_BUFSIZ) { ///+ 当前缓存已达最大值,直接处理好已添加的方法
                ///+ 准备方法列表:将待处理列表中每个分类内部的方法按照地址排序
                prepareMethodLists(cls, mlists, mcount, NO, fromBundle, __func__);
                ///+ 将协议中的方法添加至类中
                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, __func__);
        rwe->methods.attachLists(mlists + ATTACH_BUFSIZ - mcount, mcount);
        if (flags & ATTACH_EXISTING) {
            flushCaches(cls, __func__, [](Class c){
                // constant caches have been dealt with in prepareMethodLists
                // if the class still is constant here, it's fine to keep
                return !c->cache.isConstantOptimizedCache();
            });
        }
    }  

    ///+ 将这些属性添加到类中(逆序添加到待处理列表中的,所以前边部分为空)
    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
        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();
    }
}
复制代码

其实很简单,虽然分为 0>1, 1>n, n>n 三种情况,但总是将需要添加的方法添加到数组的头部的。

因此,分类中的方法会覆盖类本身的同名方法,同时后加载分类会部分先加载分类的同名方法。

这是因为后添加的方法在数组前边,而方法调用时是从前往后找的。

那么如果我们要使用原本的方法,我们可以从方法后边往前查找。

比如这样

@implementation SomeObject

- (void)testMethod {
    NSLog(@"执行了原始方法");
}

@end


@implementation SomeObject (Cat)

- (void)testMethod {
    NSLog(@"执行了分类方法");
}

@end


...
unsigned int count;
Method *methods = class_copyMethodList(SomeObject.class, &count);
for (int i = count - 1; i >= 0; --i) {
    Method m = methods[i];
    if (method_getName(m) == @selector(testMethod)) {
        IMP imp = method_getImplementation(m);
        void (*func)(id) = (void *)imp;
        func(obj);
        break;
    }
}
...
复制代码

image.png

第九部分:实现即时类(非延时类)

什么叫非延时类呢?有些类是必须要在加载镜像时立马加载的,而有些类可以晚些时候再加载(真正用到的时候再加载)。

  • 实现了 +load 方法的类是为即时类
  • 未实现类则为延迟类
// Realize non-lazy classes (for +load methods and static instances)
///+ 实现即时类(非延迟类)
for (EACH_HEADER) {
    ///+ 获取即时类列表
    classref_t const *classlist = hi->nlclslist(&count);
    for (i = 0; i < count; i++) {
        ///+ 获取真实的类
        Class cls = remapClass(classlist[i]);
        if (!cls) continue;

        ///+ 将类与其元类添加到所有类的表,之所以会添加元类,因为第二个参数默认为true
        addClassTableEntry(cls);

        ///+ 实现非 swift 类
        realizeClassWithoutSwift(cls, nil);
    }
}
复制代码
  1. 首先将类及其元类添加至所有类的哈希表中
  2. 然后实现该类

很明显,类的实现操作实在 realizeClassWithSwift 方法中:

///+ 执行类的首次初始化流程,包括开启 rw 空间,但不包括任何 swift 的初始化流程
///+ @return 返回该类的真实结构体
static Class realizeClassWithoutSwift(Class cls, Class previously)
{
    lockdebug::assert_locked(&runtimeLock);
  
    class_rw_t *rw;
    Class supercls;
    Class metacls;

    if (!cls) return nil;
    ///+ 如果该类已被实现,验证这个类
    if (cls->isRealized()) {
        validateAlreadyRealizedClass(cls);
        return cls;
    }
    ASSERT(cls == remapClass(cls));

    ///+ 获取只读 ro
    auto ro = cls->safe_ro();
    ///+ 是否为元类
    auto isMeta = ro->flags & RO_META;
    if (ro->flags & RO_FUTURE) {
        // This was a future class. rw data is already allocated.
        ///+ future 类是一个指定类名的类,仅有 rw 空间与 RO_FUTURE 标记。已提前分配好空间,等待写入数据,适用于懒加载的类
        rw = cls->data();
        ro = cls->data()->ro();
        ASSERT(!isMeta);
        ///+ 修改其 flag。如果 falg 为后者,则修改为前者
        cls->changeInfo(RW_REALIZED|RW_REALIZING, RW_FUTURE);
    } else {
        // Normal class. Allocate writeable class data.
        ///+ 这是一个普通类,构建该类的结构体.
        rw = objc::zalloc<class_rw_t>();
        rw->set_ro(ro);
        rw->flags = RW_REALIZED|RW_REALIZING|isMeta;
        cls->setData(rw);
    }

    ///+ 将 cla.cache 初始化为空
    cls->cache.initializeToEmptyOrPreoptimizedInDisguise();
  
    ///+ 如果是元类。将 cls.cache 标记为 FAST_CACHE_META
#if FAST_CACHE_META
    if (isMeta) cls->cache.setBit(FAST_CACHE_META);
#endif

    // Choose an index for this class.
    // Sets cls->instancesRequireRawIsa if indexes no more indexes are available
    ///+ 为该类选择一个 index
    ///+ 如果没有 index 可用,标记该类及其所有子类需要原始 isa
    cls->chooseClassArrayIndex();

    // Realize superclass and metaclass, if they aren't already.
    // This needs to be done after RW_REALIZED is set above, for root classes.
    // This needs to be done after class index is chosen, for root metaclasses.
    // This assumes that none of those classes have Swift contents,
    //   or that Swift's initializers have already been called.
    //   fixme that assumption will be wrong if we add support
    //   for ObjC subclasses of Swift classes.
    ///+ 如果父类与元类还未实现,则实现他们。
    ///+ 对于根类来说,这需要在 RW_REALIZED 设置完成之后完成。
    ///+ 对于跟元类来说,这需要在类 index 设定之后完成。
    ///+ 这确保没有类包含 swift 内容,或者 swift 初始化流程早已完成。

    ///+ 实现其父类
    supercls = realizeClassWithoutSwift(remapClass(cls->getSuperclass()), nil);
    ///+ 实现其元类
    metacls = realizeClassWithoutSwift(remapClass(cls->ISA()), nil);

#if SUPPORT_NONPOINTER_ISA
    if (isMeta) {
        // Metaclasses do not need any features from non pointer ISA
        // This allows for a faspath for classes in objc_retain/objc_release.
        ///+ 如果是元类,设置该类需要原始 isa
        ///+ 元类不需要任何 non-pointer 的特性
        ///+ 这可以让这些类可以快速 retain 与 release
        cls->setInstancesRequireRawIsa();
    } else {
        // Disable non-pointer isa for some classes and/or platforms.
        // Set instancesRequireRawIsa.
        ///+ 某些平台上,一些特殊的类需要禁用 non-pointer
        bool instancesRequireRawIsa = cls->instancesRequireRawIsa();
        bool rawIsaIsInherited = false;
        static bool hackedDispatch = false;
        if (DisableNonpointerIsa) {
            // Non-pointer isa disabled by environment or app SDK version
            instancesRequireRawIsa = true;
        }
        else if (!hackedDispatch  &&  0 == strcmp(ro->getName(), "OS_object"))
        {
            // hack for libdispatch et al - isa also acts as vtable pointer
            hackedDispatch = true;
            instancesRequireRawIsa = true;
        }
        else if (supercls  &&  supercls->getSuperclass()  &&
                 supercls->instancesRequireRawIsa())
        {
            // This is also propagated by addSubclass()
            // but nonpointer isa setup needs it earlier.
            // Special case: instancesRequireRawIsa does not propagate
            // from root class to root metaclass
            instancesRequireRawIsa = true;
            rawIsaIsInherited = true;
        }

        ///+ 将该类与其所有子类都设置为禁用 non-pointer
        ///+ 在以上三种情况适用:
        ///+    1. non-pointer 被环境变量或 sdk 禁用
        ///+    2. libdispatch 被攻击了(此时 isa 依然可作为虚函数表指向)
        ///+    3. 该类的父类需要禁用 non-pointer
        if (instancesRequireRawIsa) {
            cls->setInstancesRequireRawIsaRecursively(rawIsaIsInherited);
        }
    }

// SUPPORT_NONPOINTER_ISA
#endif

    // Update superclass and metaclass in case of remapping
    ///+ 设置父类
    cls->setSuperclass(supercls);
    ///+ 将该类 isa 指向元类
    cls->initClassIsa(metacls);

    // Reconcile instance variable offsets / layout.
    // This may reallocate class_ro_t, updating our ro variable.
    ///+ 如果存在父类且不是元类,则使实例变量的偏移与内部布局规范化
    ///+ 这可能会重新分类 ro 的内存空间,并更新 ro 变量
    ///+    如果该类的 ro 的起始地址 【大于】 父类 ro 的起始地址,需要将该类的 ro 顺位移动
    if (supercls  &&  !isMeta) reconcileInstanceVariables(cls, supercls, ro);  

    // Set fastInstanceSize if it wasn't set already.
    ///+ 设置其 instanceSize
    cls->setInstanceSize(ro->instanceSize);  

    // Copy some flags from ro to rw
    ///+ 将 has_cxx+dtor 与 has_cxx_ctor 标记按需拷贝至 rw
    if (ro->flags & RO_HAS_CXX_STRUCTORS) {
        cls->setHasCxxDtor();
        if (! (ro->flags & RO_HAS_CXX_DTOR_ONLY)) {
            cls->setHasCxxCtor();
        }
    }  

    // Propagate the associated objects forbidden flag from ro or from
    // the superclass.
    ///+ 从父类继承 【禁用关联对象】标记
    if ((ro->flags & RO_FORBIDS_ASSOCIATED_OBJECTS) ||
        (supercls && supercls->forbidsAssociatedObjects()))
    {
        rw->flags |= RW_FORBIDS_ASSOCIATED_OBJECTS;
    }

    // Connect this class to its superclass's subclass lists
    if (supercls) {
        ///+ 将该类添加至父类的 ext_rw 中的 firstSubclass,原本的 firstSubclass 移动至 nextSiblingClass
        ///+ 并从父类继承一些标记:
        ///+    FAST_CACHE_HAS_CXX_CTOR: 是否存在 C++ 构造方法
        ///+    FAST_CACHE_HAS_CXX_DTOR: 是否存在 C++ 析构方法
        ///+    FAST_CACHE_HAS_CUSTOM_DEALLOC_INITIATION: 是否存在自定义的 dealloc 方法
        ///+    RW_NOPREOPT_CACHE: 是否允许 cache 预优化
        ///+    RW_NOPREOPT_SELSFAST_CACHE_REQUIRES_RAW_ISA: 是否禁用 non-pointer
        addSubclass(supercls, cls);
    } else {
        ///+ 根类:与普通类类似,但 firstSubclass 为全局变量
        addRootClass(cls);
    }

    // Attach categories
    ///+ 将类 ro 中的方法,属性,协议均添加至 rw.ext
    ///+ 从 objc::unattachedCategories 根据目标类查找分类并附加到类中
    methodizeClass(cls, previously);  

    return cls;
}
复制代码
  1. 构建类的结构体
  2. 将类的 cache 初始化为 empty
  3. 如果是元类,打上 FAST_CACHE_META 标记
  4. 为该类选择一个 index。若无可用 index,标记该类及其所有子类禁用 non-pointer
  5. 优先实现其父类与元类
  6. 根据需要可能会禁用 non-pointer
    • non-pointer 被环境变量或 sdk 禁用
    • libdispatch 等库被攻击了(此时 isa 依然可作为虚函数表指向)
    • 该类的父类需要禁用 non-pointer
  7. 设置其父类
  8. 将该类的 isa 指向其元类
  9. 根据父类的 instanceSize 按需移动成员变量的内存地址
  10. 设置其 instanceSize()
  11. 将 ro 中的 has_cxx+dtor 与 has_cxx_ctor 标记到 rw 中
  12. 从父类继承 【禁用关联对象】标记
  13. 将该类添加至其父类的子类列表(如果是根类,则添加到全局的根类列表中),并继承一些标记:
    • FAST_CACHE_HAS_CXX_CTOR: 是否存在 C++ 构造方法
    • FAST_CACHE_HAS_CXX_DTOR: 是否存在 C++ 析构方法
    • FAST_CACHE_HAS_CUSTOM_DEALLOC_INITIATION: 是否存在自定义的 dealloc 方法
    • RW_NOPREOPT_CACHE: 是否允许 cache 预优化
    • RW_NOPREOPT_SELSFAST_CACHE_REQUIRES_RAW_ISA: 是否禁用 non-pointer
  14. 将 ro 中的方法,属性,协议均添加至 rw.ext,并从 objc::unattachedCategories 根据目标类查找分类并附加到类中

再看一眼 methodizeClass,从名字看,将方法真正添加至类中:

static void methodizeClass(Class cls, Class previously)
{
    lockdebug::assert_locked(&runtimeLock);

    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.
    method_list_t *list = ro->baseMethods;
    if (list) {
        prepareMethodLists(cls, &list, 1, YES, isBundleClass(cls), nullptr);
        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);
    }  

    // Root classes get bonus method implementations if they don't have
    // them already. These apply before category replacements.
    ///+ 根元类添加没什么用的 initilize 方法
    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);
}
复制代码

这方法没什么好说的,将 ro 与协议中的方法,属性,协议都添加到 rw.ext 中。

第十部分:实现待处理列表
///+ 实现上边添加的稍后处理类列表中的所有类
if (resolvedFutureClasses) {
    for (i = 0; i < resolvedFutureClassCount; i++) {
        Class cls = resolvedFutureClasses[i];
        ///+ 实现该类
        realizeClassWithoutSwift(cls, nil);
        ///+ 标记该类及其所有子类均禁用 non-pointer
        cls->setInstancesRequireRawIsaRecursively(false/*inherited*/);
    }
    free(resolvedFutureClasses);
}
复制代码

这个 resolvedFutureClasses 是在 第三部分:加载类 中。

load_image

load_image 是在镜像加载完成时回调的。主要用于调用所有类与分类的 +laod 方法。

void
load_images(const char *path __unused, const struct mach_header *mh)
{
    ///+ 已经完成了 dyld 通知注册,可以开始加载分类了
    if (!didInitialAttachCategories && didCallDyldNotifyRegister) {
        didInitialAttachCategories = true;
        ///+ 加载分类
        loadAllCategories();
    }  

    // Return without taking locks if there are no +load methods here.
    ///+ 通过判断镜像内的 类数量与分类数量 决定
    if (!hasLoadMethods((const headerType *)mh)) return;  

    recursive_mutex_locker_t lock(loadMethodLock);

    // Discover load methods
    {
        mutex_locker_t lock2(runtimeLock);
        ///+ 准备所有类与分类的 +load 方法
        ///+ 将其以 {cls,+load} 的结构体加入到数组中
        prepare_load_methods((const headerType *)mh);
    }  

    // Call +load methods (without runtimeLock - re-entrant)
    ///+ 调用准备好的 load 方法
    call_load_methods();
}
复制代码
prepare_load_methods 准备 load 方法
void prepare_load_methods(const headerType *mhdr)
{
    size_t count, i;

    lockdebug::assert_locked(&runtimeLock);

    ///+ 获取所有即时类(非延时类)
    classref_t const *classlist =
        _getObjc2NonlazyClassList(mhdr, &count);
    for (i = 0; i < count; i++) {
        ///+ 以【父类 > 子类】的顺序将 调用 add_category_to_loadable_list 将结构体 {cls, +load} 加入到数组中
        schedule_class_load(remapClass(classlist[i]));
    }

    ///+ 获取所有即时分类(非延时分类)
    category_t * const *categorylist = _getObjc2NonlazyCategoryList(mhdr, &count);
    for (i = 0; i < count; i++) {
        category_t *cat = categorylist[i];
        Class cls = remapClass(cat->cls);
        ///+ 忽略 weak-linked 类的分类
        if (!cls) continue;  // category for ignored weak-linked class

        ///+ 实现该分类的目标类(如果还没实现)
        realizeClassWithoutSwift(cls, nil);
        ASSERT(cls->ISA()->isRealized());
        ///+ 将结构体 {cls, +load} 加入到数组中
        add_category_to_loadable_list(cat);
    }
}


///+ 确保 【父类 > 子类】的顺序
///+ 将该类标记为 RW_LOADED (1<<23)
static void schedule_class_load(Class cls)
{
    if (!cls) return;
    ASSERT(cls->isRealized());  // _read_images should realize

    if (cls->data()->flags & RW_LOADED) return;

    // Ensure superclass-first ordering
    schedule_class_load(cls->getSuperclass());  

    add_class_to_loadable_list(cls);
    ///+ 将该类标记为 RW_LOADED (1<<23)
    cls->setInfo(RW_LOADED);
}


void add_class_to_loadable_list(Class cls)
{
    IMP method;  

    lockdebug::assert_locked(&loadMethodLock);  

    ///+ 根据 name 匹配获取 load 方法
    method = cls->getLoadMethod();
    if (!method) return;  // Don't bother if cls has no +load method
   
    ///+ 根据需要扩容,初始值为 16
    if (loadable_classes_used == loadable_classes_allocated) {
        loadable_classes_allocated = loadable_classes_allocated*2 + 16;
        loadable_classes = (struct loadable_class *)
            realloc(loadable_classes,
                              loadable_classes_allocated *
                              sizeof(struct loadable_class));
    }
    
    ///+ 将 {cls, +load} 放入数组末尾
    loadable_classes[loadable_classes_used].cls = cls;
    loadable_classes[loadable_classes_used].method = method;
    loadable_classes_used++;
}
复制代码

将所有即时类(非即时类)和即时分类(非延时分类)的目标类 按照 【父类 > 子类】的顺序把 {cls, +load} 添加到数组中,并将这些类标记为 RW_LOADED(1<< 23)

call_load_methods 执行 +load 方法
void call_load_methods(void)
{
    static bool loading = NO;
    bool more_categories;  

    lockdebug::assert_locked(&loadMethodLock);

    // Re-entrant calls do nothing; the outermost call will finish the job.
    ///+ 重复调不做任何事情;最外层调用将会完成操作
    if (loading) return;
    loading = YES;  

    ///+ 创建一个自动释放池
    void *pool = objc_autoreleasePoolPush();
  
    do {
        // 1. Repeatedly call class +loads until there aren't any more
        ///+ 1. 执行已经添加到 loadable_classes 中的所有 load 方法
        ///+    call_class_load 会将现有 loadable_classes 分离出来用于执行,
        ///+    新加的部分会在原有部分执行完成之后放入到新的 loadable_classes 中
        while (loadable_classes_used > 0) {
            call_class_loads();
        }
  
        // 2. Call category +loads ONCE
        ///+ 2. 执行已经添加到 loadable_categories 中所有的 load 方法
        ///+    call_category_loads 会将现有 loadable_categories 分离出来用于执行,
        ///+    内部会将新加的部分在原有部分执行完成之后会重新放入到新的 loadable_categories
        more_categories = call_category_loads();
  
        // 3. Run more +loads if there are classes OR more untried categories
        ///+ 3 执行新加的部分
    } while (loadable_classes_used > 0  ||  more_categories);

    ///+ 释放该自动释放池中的对象并释放该自动释放池
    objc_autoreleasePoolPop(pool);  
    
    loading = NO;
}
复制代码
call_class_loads: 执行类的 load 方法
static void call_class_loads(void)
{
    int i;
    
    // Detach current loadable list.
    ///+ 将现有的列表分离出来
    struct loadable_class *classes = loadable_classes;
    int used = loadable_classes_used;
    loadable_classes = nil;
    loadable_classes_allocated = 0;
    loadable_classes_used = 0;
    
    // Call all +loads for the detached list.
    for (i = 0; i < used; i++) {
        Class cls = classes[i].cls;
        load_method_t load_method = (load_method_t)classes[i].method;
        if (!cls) continue; 
        
        ///+ 执行 load 方法
        (*load_method)(cls, @selector(load));
    }
    
    // Destroy the detached list.
    ///+ 销毁分离出来的列表
    if (classes) free(classes);
}
复制代码

将现有列表分离出来,执行后销毁。这不会影响执行期间新加入的部分

call_category_loads:执行分类的 load 方法
static bool call_category_loads(void)
{
    int i, shift;
    bool new_categories_added = NO;
    
    // Detach current loadable list.
    ///+ 将现有的分离出来
    struct loadable_category *cats = loadable_categories;
    int used = loadable_categories_used;
    int allocated = loadable_categories_allocated;
    loadable_categories = nil;
    loadable_categories_allocated = 0;
    loadable_categories_used = 0;

    // Call all +loads for the detached list.
    ///+ 执行现有的这部分分类的 load
    for (i = 0; i < used; i++) {
        Category cat = cats[i].cat;
        load_method_t load_method = (load_method_t)cats[i].method;
        Class cls;
        if (!cat) continue;  

        cls = _category_getClass(cat);
        ///+ 只有在类已注册的情况下才能执行
        ///+ 执行后将记录的 cat 置空
        if (cls  &&  cls->isLoadable()) {
            (*load_method)(cls, @selector(load));
            cats[i].cat = nil;
        }
    }  

    // Compact detached list (order-preserving)
    ///+ 未执行 laod 方法的记录重新放入列表
    shift = 0;
    for (i = 0; i < used; i++) {
        if (cats[i].cat) {
            cats[i-shift] = cats[i];
        } else {
            shift++;
        }
    }
    used -= shift;

    // Copy any new +load candidates from the new list to the detached list.
    ///+ 执行期间新加入的记录追加到记录列表中
    new_categories_added = (loadable_categories_used > 0);
    for (i = 0; i < loadable_categories_used; i++) {
        ///+ 根据需要扩容
        if (used == allocated) {
            allocated = allocated*2 + 16;
            cats = (struct loadable_category *)
                realloc(cats, allocated *
                                  sizeof(struct loadable_category));
        }
        cats[used++] = loadable_categories[i];
    }  

    // Destroy the new list.
    ///+ 先释放旧的列表
    if (loadable_categories) free(loadable_categories);  

    // Reattach the (now augmented) detached list. 
    // But if there's nothing left to load, destroy the list.
    ///+ 如果存在未执行的记录,重新放入静态列表中
    ///+ 如果没有更多需要执行的记录,释放该列表
    if (used) {
        loadable_categories = cats;
        loadable_categories_used = used;
        loadable_categories_allocated = allocated;
    } else {
        if (cats) free(cats);
        loadable_categories = nil;
        loadable_categories_used = 0;
        loadable_categories_allocated = 0;
    } 

    ///+ 返回执行期间新加入到列表的数量
    return new_categories_added;
}
复制代码
  • 将现有列表分离出来得到 cats
  • 执行列表内的 load 方法,因类未注册未执行的部分放到 cats 前边
  • 将执行期间新加入的记录追加到数组 cats 中
  • 将静态列表 loadable_categories 重新指向到 cats(期间会根据需要扩容),如果没有未执行的记录,则释放静态列表
  • 返回执行期间新加入列表的数目

unmap_image

unmap_image 是在 dyld 移除镜像时回调的。用于释放内存中的镜像,具体时间是某个某个镜像不被任何进程依赖时调用。

void
unmap_image(const char *path __unused, const struct mach_header *mh)
{
    ///+ 同时锁住 loadMethodLock 与 runtimeLock
    recursive_mutex_locker_t lock(loadMethodLock);
    mutex_locker_t lock2(runtimeLock);
    
    unmap_image_nolock(mh);
}
复制代码

可以看到,除了同时锁住 loadMethodLock 与 runtimeLock 之外,仅仅只有 unmap_image_load

void

unmap_image_nolock(const struct mach_header *mh)
{  
    header_info *hi;  

    // Find the runtime's header_info struct for the image
    ///+ 从当前镜像查找 header_info
    for (hi = FirstHeader; hi != NULL; hi = hi->getNext()) {
        if (hi->mhdr() == (const headerType *)mh) {
            break;
        }
    }
    ///+ 若该镜像中不存在 header_info,也没啥好做的,直接 return
    if (!hi) return;
    
    ///+ 移除镜像内部的引用:
    _unload_image(hi);

    // Remove header_info from header list
    ///+ 从 header_info 链表中移除该镜像的 header_info
    removeHeader(hi);
    ///+ 释放该 header_info
    free(hi);
}
复制代码

所以,重点在就在 _unload_image 中:

void _unload_image(header_info *hi)
{
    size_t count, i;
  
    lockdebug::assert_locked(&loadMethodLock);
    lockdebug::assert_locked(&runtimeLock);

    // Unload unattached categories and categories waiting for +load.
    ///+ 移除未附加的分类以及等待执行 +load 的分类
    
    // Ignore __objc_catlist2. We don't support unloading Swift
    // and we never will.
    ///+ 从 “__objc_catlist” 获取分类列表
    ///+ 忽略属于 swfit 的 catlist2
    
    category_t * const *catlist = hi->catlist(&count);
    for (i = 0; i < count; i++) {
        category_t *cat = catlist[i];
        Class cls = remapClass(cat->cls);
        ///+ 忽略弱链接类的分类
        if (!cls) continue;  // category for ignored weak-linked class  

        // unattached list
        ///+ 从 unattachedCategories 列表中移除该分类
        objc::unattachedCategories.eraseCategoryForClass(cat, cls);  

        // +load queue
        ///+ 等待执行 +load 方法的分类列表中移除该分类
        remove_category_from_loadable_list(cat);
    }

    // Unload classes.
    ///+ 移除类

    // Gather classes from both __DATA,__objc_clslist
    // and __DATA,__objc_nlclslist. arclite's hack puts a class in the latter
    // only, and we need to unload that class if we unload an arclite image.
    ///+ 收集所有类  

    objc::DenseSet<Class> classes{};
    classref_t const *classlist;  

    ///+ 从 “__**objc_classlist” 收集所有类**
    classlist = _getObjc2ClassList(hi, &count);
    for (i = 0; i < count; i++) {
        Class cls = remapClass(classlist[i]);
        if (cls) classes.insert(cls);
    }  

    ///+ 从 “__**objc_nlclslist” 获取即时类(非延时类)**
    classlist = hi->nlclslist(&count);
    for (i = 0; i < count; i++) {
        Class cls = remapClass(classlist[i]);
        if (cls) classes.insert(cls);
    }  

    // First detach classes from each other. Then free each class.
    // This avoid bugs where this loop unloads a subclass before its superclass
    ///+ 首先将类彼此分离,然后释放
  
    for (Class cls: classes) {
        ///+ 从 等待执行 load 方法的列表 loadable_classes 中将类移除
        remove_class_from_loadable_list(cls);
        ///+ 从还未附加分类表 unattachedCategories 中移除该类
        ///+ 将该类与其父类分离:从父类的子类列表中移除该类
        ///+ 将该类从类映射列表中移除
        ///+ 将该类从 allocatedClasses 中移除
        
        ///+ 针对元类做上述操作
        detach_class(cls->ISA(), YES);
        ///+ 针对类本身做上述操作
        detach_class(cls, NO);
    }

    for (Class cls: classes) {
        ///+ 释放元类
        free_class(cls->ISA());
        ///+ 释放类本身
        free_class(cls);
    }
}
复制代码
  1. 移除分类
    • 未加载的分类
    • 等待执行 +load 方法的分类
  2. 移除类
    • 从等待执行 +load 方法的类列表中移除该类
    • 从还未附加分类分类表中移除该类
    • 与其父类分离:从父类的子类列表中移除该类(根类则从根类列表中移除)
    • 从类映射表中移除该类
    • 从 allocatedClasses 中移除该类
    • 释放该类

_objc_patch_root_of_class

给类打补丁,使用 replacementClass 替换 originalClass 中的一些信息,例如 isa, superclass:

void
_objc_patch_root_of_class(const struct mach_header *originalMH, void* originalClass,
                          const struct mach_header *replacementMH, const void* replacementClass)
{
    mutex_locker_t lock(runtimeLock);
    return patch_root_of_class_nolock(originalMH, originalClass, replacementMH, replacementClass);
}


static void
patch_root_of_class_nolock(const struct mach_header *originalMH, void* originalClass,
                   const struct mach_header *replacementMH, const void* replacementClass)
{
    Class originalCls = (Class)originalClass;
    Class replacementCls = (Class)replacementClass;
    
    ///+ 替换 isa(元类)
    originalCls->initIsa(replacementCls->getIsa());
    ///+ 替换 superclass
    originalCls->setSuperclass(replacementCls->getSuperclass());
    ///+ cache 初始化为空
    originalCls->cache.initializeToEmpty();  

    bool authRO = hasSignedClassROPointers((const headerType *)replacementMH);
    originalCls->bits.copyROFrom(replacementCls->bits, authRO);
}
复制代码

可以发现,这部分虽然主要是类的加载流程,但也是 app 启动流程的一部分。到这里,其实 app 就已经准备好了,可以执行 main 函数了。更多可以查看 dyld 相关原理解析。

猜你喜欢

转载自juejin.im/post/7218915344119283768