类的加载原理分析(中)

回顾

在上篇类的加载原理分析(上)我们主要是对_read_images函数到底做了哪些事情,进行了一个大致的分析,也在这个过程中找到了接下来探索的思路,也就是在readClass中通过判断类名从而筛选出我们自定义的类是如何加载的,当然大家也可以在那里进行断点调式,打印cls的类的信息,这个在之前的篇章类的原理分析有介绍类的结构,只需要按照结构就可以判断当前cls是否已经完成了ro,rw,rwe的过程,这也是我们之后定位cls加载完成的关键

一.realizeClassWithoutSwift引入

首先在_read_images中第9,10部分添加调式代码,因为这也是我们需要关注的地方,虽然通过注释也能大致猜出来,但是抱着严谨求学的态度,还是要测试一下,如图

1.类的加载处理 类实现

在这里插入图片描述
2.没有被处理的类 优化那些被侵犯的类

在这里插入图片描述
相信大家和我一样,我会认为肯定会走近断点处把😄,跑起来!!!

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        [LGPerson sayHappy];
        NSLog(@"Hello, World!");
    }
    return 0;
}

运行结果

在这里插入图片描述
OhMyGod!!!没有断进来。。。添加代码的地方都没走哦。。。
为啥呢,喝口水,压压惊😄 再试一次!!!
然后进来了。。。

在这里插入图片描述
计算机走一波:666😄

为什么2次执行结果不一样呢?答案就在这里

在这里插入图片描述
因为我给LGperson类添加了+load方法,那么为什么会这样呢?
问题1:如果我不添加load方法,那么这个类的实例方法与类方法在什么时候加载呢?

接下继续往下走就进入了realizeClassWithoutSwift

1.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
* 对类 cls 执行第一次初始化,
* 包括分配其读写数据。
* 不执行任何 Swift 端初始化。
* 返回类的真实类结构。
* 锁定:runtimeLock 必须由调用者写锁定
*****************************核心重点*****************************************/
static Class realizeClassWithoutSwift(Class cls, Class previously)
{
    runtimeLock.assertLocked();

    class_rw_t *rw;
    Class supercls;
    Class metacls;

    if (!cls) return nil;
    if (cls->isRealized()) {
        validateAlreadyRealizedClass(cls);
        return cls;
    }
    ASSERT(cls == remapClass(cls));
    // 验证类不在共享缓存的un-dlopened 部分?
    // fixme verify class is not in an un-dlopened part of the shared cache?

    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 {//普通类 分配可写的类数据  rw的写入
        // Normal class. Allocate writeable class data. ro -> rw
        rw = objc::zalloc<class_rw_t>();
        rw->set_ro(ro);
        rw->flags = RW_REALIZED|RW_REALIZING|isMeta;
        cls->setData(rw);
    }

    cls->cache.initializeToEmptyOrPreoptimizedInDisguise();

#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
    cls->chooseClassArrayIndex();

    if (PrintConnecting) {
        _objc_inform("CLASS: realizing class '%s'%s %p %p #%u %s%s",
                     cls->nameForLogging(), isMeta ? " (meta)" : "", 
                     (void*)cls, ro, cls->classArrayIndex(),
                     cls->isSwiftStable() ? "(swift)" : "",
                     cls->isSwiftLegacy() ? "(pre-stable swift)" : "");
    }

    // 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.
    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.
        cls->setInstancesRequireRawIsa();
    } else {
        // Disable non-pointer isa for some classes and/or platforms.
        // Set instancesRequireRawIsa.
        bool instancesRequireRawIsa = cls->instancesRequireRawIsa();
        bool rawIsaIsInherited = false;
        static bool hackedDispatch = false;

        if (DisableNonpointerIsa) { //环境变量是否使用Non-pointer isa
            // 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;
        }

        if (instancesRequireRawIsa) {
            cls->setInstancesRequireRawIsaRecursively(rawIsaIsInherited);
        }
    }
// SUPPORT_NONPOINTER_ISA
#endif

    // Update superclass and metaclass in case of remapping
    cls->setSuperclass(supercls);
    cls->initClassIsa(metacls);

    // Reconcile instance variable offsets / layout.
    // This may reallocate class_ro_t, updating our ro variable.
    if (supercls  &&  !isMeta) reconcileInstanceVariables(cls, supercls, ro);

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

    // Copy some flags from ro to 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) {
        addSubclass(supercls, cls);
    } else {
        addRootClass(cls);
    }
    

    // Attach categories
    methodizeClass(cls, previously);

    return cls;
}

通过最后的函数methodizeClass去附加类别,可以看到类的rw写入就是在这里执行的,而且在下面有这2句代码:

    supercls = realizeClassWithoutSwift(remapClass(cls->getSuperclass()), nil);
    metacls = realizeClassWithoutSwift(remapClass(cls->ISA()), nil);

可以看出这里是递归获取的cls关联类的信息,全部都要设置rw返回,参考isa走位图也能明白之间的执行顺序,从当前类->父类->元类->依次类推

2.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();
    
    //第一次系统化
    // Methodizing for the first time
    if (PrintConnecting) {
        _objc_inform("CLASS: methodizing class '%s' %s", 
                     cls->nameForLogging(), isMeta ? "(meta)" : "");
    }
	//安装类自身实现的方法和属性。
    // 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);
    }
	。。。。。。。
}

通过上面的注释可以看出从这里开始就是给类添加自身的属性方法了

3.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;

    if (baseMethods) {
        ASSERT(cls->hasCustomAWZ() && cls->hasCustomRR() && cls->hasCustomCore());
    } else if (cls->cache.isConstantOptimizedCache()) {
        cls->setDisallowPreoptCachesRecursively(why);
    } else if (cls->allowsPreoptInlinedSels()) {
#if CONFIG_USE_PREOPT_CACHES
        SEL *sels = (SEL *)objc_opt_offsets[OBJC_OPT_INLINED_METHODS_START];
        SEL *sels_end = (SEL *)objc_opt_offsets[OBJC_OPT_INLINED_METHODS_END];
        if (method_lists_contains_any(addedLists, addedLists + addedCount, sels, sels_end - sels)) {
            cls->setDisallowPreoptInlinedSelsRecursively(why);
        }
#endif
    }

//将方法列表添加到数组。
//重新分配不固定的方法列表。
//新方法在方法列表数组前面。
//说白了排序

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

	//如果类已经初始化,就扫描实现方法,没有就objc::CoreScanner::scanAddedMethodLists处理掉
    if (cls->isInitialized()) {
        objc::AWZScanner::scanAddedMethodLists(cls, addedLists, addedCount);
        objc::RRScanner::scanAddedMethodLists(cls, addedLists, addedCount);
        objc::CoreScanner::scanAddedMethodLists(cls, addedLists, addedCount);
    }
}

4.fixupMethodList

修复选择器

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
    //在附件方法列表中修复较少的锁?
//dyld3可能已经唯一化了列表,但没有排序
    if (!mlist->isUniqued()) {
        mutex_locker_t lock(selLock);
    
        // Unique selectors in list.
        for (auto& meth : *mlist) {
            const char *name = sel_cname(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);
        //按照选择器地址排序,给sel排序  按照地址排序
    }
    // Mark method list as uniqued and sorted.
    // Can't mark small lists, since they're immutable.
    if (!mlist->isSmallList()) {
        mlist->setFixedUp();
    }
}

通过注释可以看出这里是中间的method_t::SortBySELAddress sorter是进行排序的关键,那么这里我们可以做一下验证,看下方法的顺序是否真的有所调整?代码如下

在这里插入图片描述
在排序前和排序后分别打印方法名称即可
在这里插入图片描述
但是千万不要忘了,在fixupMethodList函数之前的某个地方,要先断点筛选出当前加载的类是我们自定义类才行哦,像methodizeClassreadClass等函数都行,然后就是给类添加属性和方法,代码如下

int main(int argc, const char * argv[]) {
    @autoreleasepool {

        [LGPerson sayHappy];
        LGPerson *p = [LGPerson alloc];
        [p cate_instanceMethod2];
        NSLog(@"Hello, World!");
    }
    return 0;
}

在这里插入图片描述
可以看到后面的顺序发生了改变,是按照地址排序的

在这里插入图片描述
可是也有个疑问2:就是属性生成的setter,getter方法的地址好像会变化,而且通过测试发现对象方法也就是存在类的方法,在这里的顺序是排好的,前后是一样的顺序,但是元类也就是类方法的顺序确实是发生了变化,所以暂时得出的结论是:
类的方法顺序并没有在这里发生变化,因为排序之前的地址已经排好序了,但是也验证了fixupMethodList函数的排序功能

二.懒加载类和非懒加载类

通过上面的测试也可以看出:普通类的实现里面加了 +load方法 ,该类就是非懒加载类,不然就是懒加载类

在这里插入图片描述
其实懒加载类的方法查找流程,我们在之前的篇章其实就走过,也就是在类第一次发送消息的时候去加载类的信息时,会进行消息的慢速查找(lookUpImpOrForward),篇章:消息的快速慢速查找

而非懒加载类则是dyld加载应用程序的时map_images加载所有非懒加载类的数据(未完成)

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
SpringBoot的运行原理可以分为以下几个方面: 1️⃣ 父依赖:SpringBoot项目的pom.xml文件会引入一个父依赖,这个父依赖包含了一些常用的依赖和插件,简化了项目的配置和构建过程。 2️⃣ starter场景启动器:SpringBoot提供了一系列的starter依赖,每个starter都包含了一组相关的依赖和配置,可以方便地引入和配置需要的功能。 3️⃣ 主启动类:SpringBoot项目的主启动类使用@SpringBootApplication注解进行标注,这个注解表示这是一个Spring Boot应用。在主启动类,通过调用SpringApplication.run方法来启动Spring Boot应用。 4️⃣ spring.factories:Spring Boot使用spring.factories文件来配置自动装配的类。这个文件定义了一些自动配置类的全限定名,当应用启动时,Spring Boot会根据这些配置来自动装配相应的功能。 5️⃣ SpringApplication.run分析:SpringApplication.run方法是Spring Boot应用的入口,它会创建一个SpringApplication实例,并根据配置来启动Spring Boot应用。在启动过程,会配置文件、创建Spring容器、执行自动装配等操作。 总结起来,SpringBoot的原理可以概括为通过父依赖、starter场景启动器、主启动类、spring.factories和SpringApplication.run方法来简化和自动化Spring应用的配置和启动过程。\[1\] \[2\] \[3\] #### 引用[.reference_title] - *1* *2* [SpringBoot运行原理分析](https://blog.csdn.net/gaowenhui2008/article/details/130456549)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] - *3* [Springboot工作原理详解](https://blog.csdn.net/huangtenglong/article/details/127862112)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值