16.iOS底层学习之分类的加载

前面我们学习了分类已经分类的加载流程,这一篇我们更细致的研究类和分类之间加载过程的联系,更加细致的思考一些上一篇值得深入的问题:
1、rw、ro、rwe的区别和联系,为什么要这么设计?
2、类和分类搭配加载过程。

rw、ro、rwe的区别和联系

根据WWDC2020了解到rw以及rwe的作用,下面我们分别来看一下ro、rw、rwe。要了解他们,我们先要了解下iOS的两种内存clean memorydirty memory
clean memory:是指加载后不会发生改变的内存。class_ro_t 就属于clean memory, 因为它是只读的(ro 代表 readonly)。
dirty memory:是指在进程运行时会发生更改的内存,class_rw_t 是 dirty memory(rw 代表 read write)。
dirty memory要比clean memory更加昂贵,前者一经使用就必须存在,可以通过runtime往类里写入新的数据,所以dirty memory是动态的,而clean memory是经过编译就不可以再写入的,大小是固定的,是可移除的,可以节省更多的内存空间,并且iOS是不会使用swap进行转换,所以dirty memory在iOS的开销更大一些,也就是所谓的更加"昂贵"。

也是因为这两种内存的存在,所以类的数据就被分成了相应的这两个部分:

  • 一经编译,就不可修改的数据。(ro)
  • 运行过程中可修改的数据。(rw)

ro:数据是只读的,它属于clean Memory。它是从沙盒读取,ro的数据在编译的时候就已经确定了。
rw:数据是可读可写的,它属于dirty Memory。rw的数据存放的是运行时动态修改的数据。
rwe:是苹果为rw做的优化,从rw拆出来那些平时不常用的部分,以减少rw的开销,大约 90% 的类从来不需要这些扩展数据,这在系统范围内可节省大约14MB 的内存。

所以综上,rw是针对于脏内存进行数据存储,ro是clean内存,而rwe是rw的优化,可以理解为他的扩展,之所以这么设计,是为了保证clean的数据越多越好,尽可能的不被污染,那么效率也就更高,如果动态添加的数据和一开始编译时期就被加载的数据都混合在一起,那么每次去查询都是去查询一个动态的空间,空间的size等都是变量,肯定就没有一块固定的不变的内存查询效率高。他这样做尽可能的保证有一块内存空间是固定的干净的,就优化了效率节省了一部分的查询时间。(一部分是个人理解)

ro-rw-rwe.png

类和分类搭配的加载过程

1.非懒加载类+非懒加载的分类
  • 非懒加载类流程:map_images->map_images_nolock->_read_images->realizeClassWithoutSwift->methodizeClass->attachToClass->load_images->loadAllCategories->load_categories_nolock->attachCategories->attachLists

调试流程,分别在attachToClass和attachCategories为类和分类做好相应的断点,筛选下为想要的类的时候,进行进一步的断点调试。

2.懒加载类+非懒加载的分类

map_images->map_images_nolock->_read_images->realizeClassWithoutSwift->methodizeClass->attachToClass
这个时候通过调试发现,并没有走attachCategories的逻辑。

3.非懒加载类+懒加载的分类

调试结果竟然和情况2是一样的!

4.懒加载类+懒加载的分类

_objc_msgSend_uncached->lookUpImpOrForward->realizeAndInitializeIfNeeded_locked->initializeAndLeaveLocked->initializeAndMaybeRelock->realizeClassMaybeSwiftAndUnlock->realizeClassMaybeSwiftMaybeRelock->realizeClassWithoutSwift->methodizeClass->attachToClass

以上只讨论了单个类的情况,那么如果有多个分类都实现了load呢?接着调试看下。

懒加载类+多个非懒加载分类
  • 一共三个分类,一个实现了+load一个不实现,此时调试走的逻辑是:
    map_images->map_images_nolock->_read_images->realizeClassWithoutSwift->methodizeClass->attachToClass。
    没有调用attachCategories的逻辑!

  • 一共三个分类,两个实现了+load一个不实现,此时调试走的逻辑是:
    load_images->prepare_load_methods->realizeClassWithoutSwift->methodizeClass->attachToClass->attachCategories
    这个走了attachCategories逻辑!

  • 一共四个分类,分类B和分类C都实现了-(void)BCBC方法,B的实现打印的是"BCBC",C的实现打印的是"CBCB",分类A和分类B实现+load,分类C、D不实现:load_images->prepare_load_methods->realizeClassWithoutSwift->methodizeClass->attachToClass->attachCategories,走了attachCategories逻辑!。并且分类的打印情况如下:

    image.png

    并且换一下分类B和分类C的编译顺序,则最后会打印后边编译的分类同名方法,即使其中一个不用声明,光有实现,也会打印后编译的分类中的实现内容。(这个过程后面有时间具体探索和分析下,这个部分先略过)

所以经过上面的测试,发现类不实现+load,分类至少两个实现了+load以上就会走prepare_load_methods->realizeClassWithoutSwift->methodizeClass->attachToClass->attachCategories流程。

非懒加载类+多个非懒加载分类
  • 一共四个分类A、B、C、D都不实现+load,主类实现了+load,不会走attachCategories逻辑。
  • 一共四个分类只有A实现了+load,主类实现了+load,这时候会走attachCategories逻辑,并且通过loadAllCategories走到attachCategories。
  • 一共四个分类A、B实现了+load,主类实现了+load,这时候会走attachCategories逻辑,并且也是通过loadAllCategories走到attachCategories。

所以非懒加载类,只要有一个分类实现了+load,会通过loadAllCategories来到attachCategories

emmm为什么会这样啊?分类的数量还有实现+load会影响分类的加载流程,会走不同的方法进入到attachCategories,或者有的即使实现了分类和+load也不会来到attachCategories!!!!😳😳😳😳😳😳😳😳

我们通过查看这几种情况下的ro、rw或者rwe中method_list情况来观察下。

  • 第一种情况,分类有+load而主类没有,是没有走attachCategories的逻辑情况,此时我们通过调试ro,发现在_read_images方法中,尽管主类没有实现+load,但是还是走了非懒加载的方法。

    image.png

    并且通过打印调试ro中的baseMethod的内容发现,此时分类的方法已经全都被加到了baseMethod中。
    image.png

    所以,这种情况,是在dyld的时候就直接把分类的方法合并到了ro的baseMethod里。

  • 而主类有+load,分类没有的情况通过调试和上面👆🏻这个过程一样,也是ro中的baseMethodList已经有了分类中的方法,不需要再attachCategories都。

  • 然后分类和主类都没实现+load的情况,上面的结论是也不再走attachCategories,来到realizeClassWithoutSwift打印ro的数据一样也是分类的方法已经添加完了,只是发起的流程由lookupImpOrForward发起的了。

  • 最后,多个分类不全有+load的情况,主类实现了+load,通过调试,发现分类的方法并没有全部加入到ro的baseMethod中,此时只有6个。

    image.png

    然后我打印下这六个方法分别是:
    image.png

    都是主类LGPerson的方法,此时分类的方法并没有加进来!

然后我们接着调试,来到方法methodizeClass,此时rwe打印还是nil,而方法methodizeClass基本都是从rwe取数据,所以这个时候分类的信息还没有被加载进来,然后继续调试就来到了load_images->loadAllCategories->load_categories_nolock,然后通过attachCategories初始化了rwe!

image.png

然后就接着去走prepareMethodLists和attachList流程。

所以这个流程相对来说比较繁琐,会多走一些方法来把分类的方法插入到methodList,并且要创建rwe,系统开销比前几种会大一些。所以分类中的+load要慎用,会影响启动速度,增加不必要的开销!


学习本身就是逆人性的过程,看了两三天这个地方,头大头大,还好有收获,加油加油!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,919评论 6 502
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,567评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,316评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,294评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,318评论 6 390
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,245评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,120评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,964评论 0 275
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,376评论 1 313
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,592评论 2 333
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,764评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,460评论 5 344
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,070评论 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,697评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,846评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,819评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,665评论 2 354

推荐阅读更多精彩内容