LLDB结合底层源码分析Cache数据结构

我再来分享一个底层知识点,学到了之后不写出来总觉得不是自己的,关于cache的数据结构,首先cache是什么呢?

这个英文单词就是缓存的意思,那他缓存的是什么呢?我们大多数人肯定都不太了解,我之前获取bits,可以进行内存平移,那我获取cache是不是也可以进行内存平移呢?

一试便知,我在objc的源码工程里面写了一个demo,自定义了一个类 LGPerson 继承自 NSObject,来到main里面,通过 class 拿到这个类,然后在下一行打一个断点,如下图。

图片

运行,停在断点处之后,我来进行万能的LLDB调试。

图片

拿到 pClass 的地址之后给他打印出来,但是直接打印是不行的,还要进行一下强转。

图片

强转之前,我先给他平移16个字节,就是加10,变成了0x0000000100008460,然后强转的话,转成什么类型呢?

就是 cache 的类型,那 cache 是什么类型我不记得了,我就去源码里面找一下,全局搜索objc_class。

图片

可以在后面加一个空格,结果会少很多,找到 new.h结尾的文件,然后里面就有这个 objc_class 结构体,点进去。

图片

这里就找到了cache,这个过程需要对底层原理有比较好的掌握,cache 是什么类型就一目了然了,是一个cache_t 类型,我拷贝出来,加个星号表示还原他的指针。

图片

回车就得到了 cache_t 类型的 $1,然后我来看里面的数据。

图片

打印得到了这么一长串内容,主要有这么几个变量名:_bucketsAndMaybeMask、_maybeMask、_flags、_occupied、_originalPreoptCache。

那这些东西我看不懂,不知所云,所以我需要在源码里面找到对应的说辞,怎么找呢,我 command 点击 cache_t,Jump to Definition。

图片

很明显,cache_t 也是一个结构体,然后有一个private,里面就有这个 _bucketsAndMaybeMask 等一些变量。

跟我打印出来的一模一样,除了 _bucketsAndMaybeMask之外,其他几个都被 union 一个大括号包进去了,他们是一个联合体。

很多人可能对于这种结构不那么了解,我再来补充一个点,在真机上的app,是运行在 arm64 架构上的,模拟器呢?i386,Mac呢?

x86_64,M1 的 Mac 也是 arm64,这是跟硬件有关的,那这个联合体里面有一个 #if LP64 是什么呢?LP64 就是适用在 Linux 和 macOS 电脑上面的一个数据模型,我找了个表格给大家看一下。

图片

就很明显了,LP64 就是指代 Unix 系列的操作系统,所以都是跑的64位,也就是说,这个 if 条件是成立的。

整个 cache_t 的数据结构就能够很清晰很直观的了解了,那么问题来了,cache是缓存,那到底缓存什么呢?要么缓存属性,要么缓存方法,这里我看到了属性,但是没看到方法。

按理说也应该缓存方法才对,如果是缓存方法的话,我应该在这个结构体里面能看到 sel 和 imp,但是并没有,是不是意味着他不是缓存这些东西呢?我先卖个关子。

接下来又有一个问题,这几个属性里面,哪一个才是我们需要关注的、最重要的那个?打印信息里面两个没有值的可以排除,剩下的我也不知道哪个最重要,那怎么办呢?

这里又有一个阅读源码的小技巧,就是看他提供的功能方法,这个cache_t,是一个缓存,也就必定是一个存储的过程,意味着他一定会有东西进行了读写,或者增删改查等等的操作。

所以,我再继续往下找方法,在481行就找到了一个 insert,也就是插入,正好命中了我刚刚的猜想,他离不开增删改查,并且他的参数正好就有 sel 和 imp。

图片

所以,我来看看他里面有没有我想要的东西,点进去,看到了两个 sel(),都是由 bucket_t 对象中的元素进行调用,也就是对 bucktet_t 进行了一些操作,难道关键就在bucket_t ?(下图源码进行了一些删减)

图片

正好在之前的 cache_t 结构体中找 insert 的时候也看到了不少 bucket_t。

图片

感觉他的重心应该就是这个bucket_t,我直接点进去,看到如下图的内容。

图片

这就恍然大明白了,就是千呼万唤的 imp 和 sel,bucket 单词是“桶”的意思,就是一个容器,装了很多的imp 和 sel,并且一个 imp 就对应了一个 sel,梳理一下。

cache 里面有一个 buckets,buckets 里面就会有一个 sel 和 imp,但是这个具体的结构,我还不是非常的清楚,但是知道这么多就已经足够了。

图片

然后我要去验证里面的值,是不是真的有,是不是真的是这样的呢?眼见不一定为实,自己操作一遍才放心,那我继续LLDB调试。

之前已经拿到了$2,然后他的重点是buckets,所以我是不是应该打印这个_bucketsAndMaybeMask 呢?只有这个里面有 buckets 这个词,试一试。

图片

然后我去拿他的Value,因为也没其他东西可以拿。

图片

竟然拿不到!又进了死胡同,LLDB调试不出来了,怎么办?这个时候又回到了上面提到的调试技巧,我只能去找他有没有合适的方法。

这是LLDB调试遇到问题的时候最常见的办法,那我去 cache_t 结构体中找一下是不是有get相关的方法,别说,还真有。

图片

这里得到了一个结构体指针 buckets,感觉事情变得简单了,我拷贝 buckets() 继续进行调试。

图片

果真拿到了一个 bucket_t 的地址,那这个地址里面是什么我也不知道,打印出来看一下。

图片

跟之前的源码分析一模一样,就是 imp 和 sel,但是他们什么都没有,很尴尬,不过也没关系,我这里已经可以得到一些信息了,bucket_t 结构体里面是有一个 #if 判断的。

图片

这个条件语句里面,我是走的 if 还是 else 呢,我都不用分析这个条件,对比一下打印出来的 $4 就知道了,先 imp,然后 sel,所以走的是 arm64,大多数人在这里应该都是走的 else,为什么呢?

因为我是M1的Mac,我已经走在了时尚的最前沿,哈哈哈,大多数人都是 x86,所以走的是 else,如果是真机环境的话,走的也是 arm64,其实区别不大,了解一下就好了。

但是他们为什么都没有呢?因为没有调用方法!没有调用方法,他有个LLDB的缓存啊,那我再来调用一下方法,这个 LGPerson 我已经写好了一个实例方法 saySomething。

图片
图片

调完方法之后,上面的流程我再来一次。

图片

imp 的 Value 不出所料有值了!但是你们操作的时候,可能还是会没有东西,如果你们没有的话,怎么办呢?

我也提一嘴,这是一个 buckets,他有个s字母结尾,就意味着他是一个数组,所以你可以在后面加上一个[1],取他下一个bucket,或许你就有值了。

如果没有多个就可以直接取,这里涉及到了哈希函数,因为哈希函数的下标是不一定的,普通的数组是从零开始的,但是哈希就不同,而且他还是无序的。

但是这个 $10 还不是我想看到的结果,我想看的是最终打印出 saySomething,才能证明我们的源码分析没有问题,那我还是同样的来看 bucket_t 结构体里面有没有相应的方法。

图片

找到一个 sel(),不解释了,直接拿过来用。

图片

打印出了 saySomething,就这?简单得很嘛,所以,我也应该同样的可以改成 imp()。

图片

竟然报错了,问题不大,这个错误提示很熟悉,翻译过来就是参数太少了,预想的是有2个参数,但是我没有给,只有0个,如果你也这么操作了,说明你肯定没有去仔细看源码,imp() 就在 sel() 下面一点点。

图片

这里他需要两个参数,一个 bucket_t,一个 class,这个 class 我可以给你,很简单,就是 pClass,那这个bucket_t 我怎么给?我不知道,我也不想知道,感觉很麻烦的亚子,那我直接丢个 nil,回车。

图片

漂亮,来自 KCObjcBuild 里面的 LGPerson 的实例方法 saySomething,完美打印出来,到这里,cache的数据结构分析,就基本搞定,到此结束。

最后呢,如果对于文章内容有任何疑问都可以进行留言,objc源码我就不放了,网上一大把,如果某些内容有误也恳请帮我指出来,共同进步。

转载:https://mp.weixin.qq.com/s/RFZ35hNotUcYlt0pEnWNHw

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

推荐阅读更多精彩内容