iOS底层-类的底层原理(一)

前言

通常在创建对象的时候,都会继承 NSObject去新建一个类,那么NSObject 继承谁?或者说类的底层原理是什么?下面来具体探究一下。

本文探索过程会涉及到 对象的本质

准备工作

  • 新建一个 Project
  • main.m 中添加一个类 ZLObject,打上断点并执行。

案例分析

1. 探索对象的底层
  • $ p obj:查看 obj 对象的地址。

  • $ x/4gx obj地址:查看 obj 对象的isa及内存占用。

  • $ p/x isa地址 & 掩码地址:与掩码做与运算

  • $ po 与运算地址:查看关联类

流程如下:

其中掩码为 __86_64__ 的掩码地址 0x00007ffffffffff8ULL,最终得到 ZLObject 的地址:0x0000000100008260

2. 继续探索

以上面 ZLObject0x0000000100008260 地址,再次进行 isaISA_MASK 与运算,最终得到 0x0000000100008238 的地址,还是 ZLObject

这就比较奇怪了,为什么都是 ZLObject,内存地址却不一样?

3. 再次探索

再次以新的 ZLObject0x0000000100008238 地址,再次进行 isaISA_MASK 与运算,最终得到 0x00007fff92c9d0f0 的地址,是 NSObject

对此,有两个疑问:

  • 两次的 ZLObject 是否存在的一定的联系?

  • NSObjectisa 指向了什么?

4. 方法印证

添加下图方法,并打印其内存情况。

打印结果如下:

通过上面的案例,得到的结论是:对象的 isa 是指向 ,也就是 ZLObject 的内存地址 0x0000000100008260,那么类的 isa 指向的是什么?为什么这块内存地址也是 ZLObject

5. MachO文件分析

有关MachO文件探索,请移步 MachO文件分析

通过 MachOView 的分析,直接定义到 Symbol Table 下查看所有的 symbols 数据,搜索 class,得出:

  • 找到了 0x0000000100008260 的内存地址,其符号下标就是 _OBJC_CLASS_$_ZLObject,也就是 ZLObject
  • 找到了 0x0000000100008238 的内存地址,其符号下标就是 _OBJC_METACLASS_$_ZLObject,称为 ZLObject 的元类。

结论一

  • 对象的 isa 指向

  • 类的 isa 指向 元类

  • 元类 是系统编译器生成的。

MetaClass的本质

上面的分析中,提出了两个疑问,其中第一个已经证实,两次的 ZLObject,一个是 ,一个是 元类。那么,NSObjectisa 指向了什么?接下来我们继续探索。

通过查看 NSObject.class 的内存地址 0x00007fff92c9d118,发现和之前的 NSObject 地址 0x00007fff92c9d0f0 不一样,因此 0x00007fff92c9d0f0NSObject元类

那么 NSObject元类isa 又指向了什么?

通过运算分析,NSObject元类isa 还是指向 NSObject元类

结论二

  • 元类isa 指向 根元类

  • 根元类isa 还是指向 根元类

  • 对于 NSObject ,它也是 根类根类isa 也是指向 根元类

SuperClass的本质

上面分析了 ZLObject 类和 NSObject类的 isa 指向情况,那么父类 SuperClassisa 指向情况和继承关系如何呢?

1. NSObject SuperClass

创建如图类,并打印其内存情况:

打印结果如下:

说明:

  • NSObject 的父类是 nil

  • NSObject 的元类的父类还是 NSObject

2. ZLObject SuperClass

首先看一下类的父类是 NSObject 的情况,打印其元类:

打印结果如下:

说明:ZLObject 的元类的父类是 NSObject 的元类

3. ZLSubObject SuperClass

创建继承于 ZLObject 的子类 ZLSubObject,并打印如图内存:

打印情况如下:

说明:ZLSubObject 的元类的父类是 ZLObject 的元类。

结论三

  • NSObject 的父类是 nil,其元类的父类还是 NSObject

  • 父类的元类也有继承关系。

最终得到两个关系图,一个是类的 isa 指向图,一个是类的继承链图。

类的 isa 指向图
类继承链图

内存偏移

1. 普通指针

打印结果:

说明:常量10处于 常量区 ,可以被 不同 的指针引用,其引用原理为 值拷贝

2. 数组指针

打印结果:

说明:

  • 使用数组 下标 取地址,和利用 指针偏移量 取值效果一样。不如上图中是 &c[0]b + 1

  • 数组的 首地址 也就是数组 第一个 元素的地址。

  • 指针 偏移量大小 和数组元素所占用 字节大小 有关,比如上图中是 int,所以打印结果地址相差 4,也称 步长

类的内存结构

分析源码可知,objc_class 方法实现如下:

其内存结构图如下:

因此如果想要得到 bits,就必须知道 superclasscache 的内存字节数,再利用内存偏移得到 bits

由于 isa8 字节,此处不再赘述。

1. superclass

superclassisa 一样,也是 8 字节,因为都是 Class 结构体类型

2. cache

cache_t 的有效代码如下:

cache_t 中的方法和 static 声明的字段都不是在该结构体内,所以只需要分析上面的有效代码,获取 cache_t 所占用的内存大小,即可得到 bits 的内存偏移量。

1. 联合体之外的 explicit_atomic<uintptr_t>的大小:
explicit_atomic 为泛型指针,所以其内存大小由 <uintptr_t> 决定的,也就是 uintptr_t 的大小,为 8 字节。也可以使用 sizeof(uintptr_t) 查看其字节大小。

2. 联合体的大小:

说明:

  • mask_tuint32_t 类型,所以为 4uint16_t 类型为 2

  • <preopt_cache_t *> 为结构体指针,所以为 8

  • 联合体内部 内存共用 ,且 互斥 的特性,所以总大小为 8

结论

cache 所占字节为 16

bits 的内存偏移量为 isa + superclass + cache,总大小为 32

类的底层数据获取

1. 获取 bits 数据:

上图可得类的首地址为:0x1000081e8,那么以类的首地址偏移 32 字节,就是 bits

此时 bits 的数据存储在 $4 中。

在上面分析 objc_class 的结构时,可以对 data() 进行存取数据。

因此,可以通过 $4->data() 方法获取 class_rw_t 内存地址:

2. 获取 class_rw_t 数据:

这样 class_rw_t 的数据就可以拿到了,但是并不知道所需的属性和方法具体存在哪里。

3. 分析 class_rw_t 结构体:

通过 properties()methods() 获取类的属性和方法。

4. 获取 property_array_tlist

5. 获取 listptr

6. 获取 property_list_t 的数据 :

7. 使用 C++数组 get() 方法,获取类的属性 :

8. 同理获取类的 methods()

最后一步的 get(0) 没有拿到数据,因此获取方法和属性不一样。

9. 分析 method_t

10. 调用 big() 函数。

类的底层探索流程图

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

推荐阅读更多精彩内容