类的加载原理(中)

1,016 阅读4分钟

回顾

前面探索到_read_images函数,并查看了类的处理,查看了readClass实际流程,发现并没有加载到rorw

realizeClass的引入

因为主要研究的是类的加载原理,所以跳过中间流程,直接查看类的加载

// 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;
            
            const char *mangledName = cls->nonlazyMangledName();
            const char *person = "LKTeacher";
            if (strcmp(mangledName, person) == 0) {
                printf("non-lazy classes  ********%s**********",mangledName);
            }
            addClassTableEntry(cls);

            if (cls->isSwiftStable()) {
                if (cls->swiftMetadataInitializer()) {
                    _objc_fatal("Swift class %s with a metadata initializer "
                                "is not allowed to be non-lazy",
                                cls->nameForLogging());
                }
                // fixme also disallow relocatable classes
                // We can't disallow all Swift classes because of
                // classes like Swift.__EmptyArrayStorage
            }
            realizeClassWithoutSwift(cls, nil);
        }
    }

通过苹果注释,我们可以明确知道,只有非懒加载才会执行下面这段代码,所以我们给LKTeacher添加+load methods,为了精准定位到LKTeacher类,我们添加前面使用过的代码

const char *mangledName = cls->nonlazyMangledName();
const char *person = "LKTeacher";
if (strcmp(mangledName, person) == 0) {
    printf("non-lazy classes  ********%s**********",mangledName);
}

打上断点,进入调试,最终执行到realizeClassWithoutSwift方法

realizeClassWithoutSwift查看

/***********************************************************************
* realizeClassWithoutSwift
* Performs first-time initialization on class cls, 
* including allocating its read-write data.
* Does not perform any Swift-side initialization.
* Returns the real class structure for the class. 
* Locking: runtimeLock must be write-locked by the caller
**********************************************************************/
static Class realizeClassWithoutSwift(Class cls, Class previously)
{
    //省略代码
    auto ro = (const class_ro_t *)cls->data(); //获取`machO文件`中的数据地址,按照`class_ro_t`格式进行强转。
    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 = objc::zalloc<class_rw_t>();//初始化`rw`,将`ro`复制给`rw`,并将`cls`的`bits.data`设置为`rw`
        rw->set_ro(ro);
        rw->flags = RW_REALIZED|RW_REALIZING|isMeta;
        cls->setData(rw);
    }

    //省略中间代码

    // Attach categories
    methodizeClass(cls, previously);

    return cls;
}

添加断点到方法开始处,打印ro image.png 可以看到baseMethodList里面并没有任何方法。realizeClassWithoutSwift最终会执行methodizeClass方法。

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->nonlazyMangledName();
    const char *person = "LKTeacher";
    if (strcmp(mangledName, person) == 0 && !isMeta) {
        printf("non-lazy classes  ********%s**********",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), 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.
    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);
}

查看整段代码,断点调试,发现rwe并没有值,所以对应的判断条件也不会进入,所以重点查看prepareMethodLists方法。

prepareMethodLists

static void prepareMethodLists(Class cls, method_list_t **addedLists, int addedCount,
                   bool baseMethods, bool methodsFromBundle, const char *why)
{
    runtimeLock.assertLocked();

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

    // If the class is initialized, then scan for method implementations
    // tracked by the class's flags. If it's not initialized yet,
    // then objc_class::setInitialized() will take care of it.
    if (cls->isInitialized()) {
        objc::AWZScanner::scanAddedMethodLists(cls, addedLists, addedCount);
        objc::RRScanner::scanAddedMethodLists(cls, addedLists, addedCount);
        objc::CoreScanner::scanAddedMethodLists(cls, addedLists, addedCount);
    }
}

查看代码,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());
            printf("未排序***%s---%p***\n",name,meth.name());
            meth.setName(sel_registerNameNoLock(name, bundleCopy));
        }
    }

    // Sort by selector address.
    // Don't try to sort small lists, as they're immutable.
    // Don't try to sort big lists of nonstandard size, as stable_sort
    // won't copy the entries properly.
    if (sort && !mlist->isSmallList() && mlist->entsize() == method_t::bigSize) {
        method_t::SortBySELAddress sorter;
        std::stable_sort(&mlist->begin()->big(), &mlist->end()->big(), sorter);
    }
    
    printf("***分界线***\n");
    for (auto& meth : *mlist) {
        const char *name = sel_cname(meth.name());
        printf("排序后***%s---%p****\n",name,meth.name());
    }
    
    // Mark method list as uniqued and sorted.
    // Can't mark small lists, since they're immutable.
    if (!mlist->isSmallList()) {
        mlist->setFixedUp();
    }
}

根据苹果注释,可以看到这里是根据selector address进行排序。因为M1设备是small lists,无法验证,如需验证,请自行调试。

非懒加载类的加载探索

类的加载方法我们找到了realizeClassWithoutSwift,在此处添加断点,并去掉LKTeacher里面的load方法,查看堆栈信息 image.png 可以发现整个流程如下,也就是类开始调用时才会加载

  • lookUpImpOrForward
  • realizeAndInitializeIfNeeded_locked
  • initializeAndLeaveLocked
  • initializeAndMaybeRelock
  • realizeClassMaybeSwiftAndUnlock
  • realizeClassMaybeSwiftMaybeRelock
  • realizeClassWithoutSwift

image.png

分类的本质探索

main文件里面添加LKPerson和分类LKPerson (LK)(为了方便编译写在一起),给分类添加属性和方法

image.png 发现_category_t里面有instance_methodsclass_methods,这是因为分类没有元类,所以实例方法和类方法,都存在分类里。查看cpp文件,并没有找到getset方法,所以可以得知,分类不能添加属性。

image.png

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;
    }
};

rwe什么时候赋值的

methodizeClass方法里面,可以看到auto rwe = rw->ext();,点进去查看ext

    class_rw_ext_t *ext() const {
        return get_ro_or_rwe().dyn_cast<class_rw_ext_t *>(&ro_or_rw_ext);
    }

    class_rw_ext_t *extAllocIfNeeded() {
        auto v = get_ro_or_rwe();
        if (fastpath(v.is<class_rw_ext_t *>())) {
            return v.get<class_rw_ext_t *>(&ro_or_rw_ext);
        } else {
            return extAlloc(v.get<const class_ro_t *>(&ro_or_rw_ext));
        }
    }

    class_rw_ext_t *deepCopy(const class_ro_t *ro) {
        return extAlloc(ro, true);
    }

找到extAllocIfNeeded()方法,全局搜索extAllocIfNeeded,发现以下方法调用

  • attachCategories
  • objc_class::demangledName
  • class_setVersion
  • addMethods_finish
  • class_addProtocol
  • _class_addProperty
  • objc_duplicateClass