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

前言

类的底层原理(一) 的探索后,已理解 isa指针指向类的结构 。下面继续探索类的底层原理,并做相应的补充。

准备工作

WWDC-关于 runtime 的改进优化

LLVM源码

成员变量的底层原理

在分析 类的底层原理(一) 时,只分析了 propertiesmethods

  • propertiesmethods 都在 class_rw_t 中。

  • 而成员变量 ivars 存在于class_rw_tro()中,也就是 class_ro_t

class_ro_t 结构如下:

案例一

通过案例分析 ivars ,添加如下代码 :

打印结果:

此时,ivars 都存在于 ivar_list_t 中,使用C++数组 get() 获取每个成员变量。

成员变量、属性、实例变量的区别

  • 成员变量在类的 {} 中,以 基本数据类型 声明的变量,例如:NSString、int、float、double、char、bool。

  • 属性是用 @property 修饰的,在底层会变成 _ 方式的 成员变量 ,也会自动生成 getset 方法。 属性 = _成员变量 + set + get

  • 实例变量是以 对象类型 声明的 (特殊的成员变量),例如 NSObject *pp 就是实例变量。

案例二

1. 创建一个 Project,添加如下代码:

2.通过 clang 编译并查看编译文件:

$ clang -rewrite-objc main.m -o main.cpp

属性在编译时会变成 _的成员变量,并生成对应的 setget 方法。

但是其 set 实现方式却不一样:

  • name 是通过 objc_setProperty 方法实现的

  • addressage 则是通过内存地址偏移的方式存储的

为什么会不一样?namecopy 修饰,猜想是和 copy 修饰符有关。

分析 objc_setProperty

为什么会有 objc_setProperty 的存在?

当创建一个 属性 时,调用 set 存数据时,不可能每创建一个 属性 就在底层生成一个对应的 set 方法,这样对内存开销太大了。于是就有了 objc_setProperty 方法,不管上层是什么 set 方法,统一调用 objc_setProperty 方法。这个过程就是 SELIMP 过程。

SEL 就是方法名字,IMP 就是底层方法实现。

由于 objc_setProperty 是需要在编译时直接创建,所以 objc_setProperty 需要去 LLVM源码 中查找。

1. 定位 objc_setProperty 方法

2. 定位 getSetPropertyFn 方法

3. 定位 GetPropertySetFunction 方法

GetPropertySetFunction 方法调用,是在 switch 选择 strategy.getKind()PropertyImplStrategy::GetSetProperty 或者 PropertyImplStrategy::SetPropertyAndExpressionGet 时执行的。

那么 strategy 是什么意思?什么时候给它赋值?每个 case 都有什么意义?

4. 分析 PropertyImplStrategy

5. PropertyImplStrategy 构造函数

结论:

  • 只要是设置了 copy 属性,不管是不是原子性,都没有影响,set 方法都会被重定向到objc_setProperty

  • 如果不设置属性(除原子性之外),那么默认属性是 strong ,不会触发 objc_setProperty

分析 objc_getProperty

同样 objc_getProperty 也需要去 LLVM源码 中查找。

1. 定位 objc_getProperty 方法

2. 定位 getGetPropertyFn 方法

3. 定位 GetPropertyGetFunction 方法

类方法的底层原理

lldb 验证流程如下:

总结:

  • 对象方法 存储在自身

  • 类方法 存储在 元类 中,并且以 对象方法 存在于 元类 中。

  • 没有所谓的 类方法 之说,所有的方法都是 对象方法,其底层都是 函数

补充:类型编码 TypeEncoding

在上面的 main.cpp 中,搜索 setName 发现有一些奇奇怪怪的符号。

其中 "v24@0:8@16""@16@0:8" 其实是 类型编码 。下面具体看一下什么是 类型编码

官网关于 TypeEncoding 的解释。归根结底,其实就是对照着下面的 符号表 去分析编码代码。

TypeEncoding符号表

那么以上面 setName 为例,具体分析 v24@0:8@16 的实际意义。

补充:面试题

1. 为什么获取 元类类方法 也可以得到 类方法?(已证明:类方法对象方法 形式存储在 元类 中)

打印结果如下:

分析底层方法:

分析得出:所谓的 类方法 其实就是获取 元类对象方法

分析 getMeta() 方法:

分析得出:如果是 元类 ,则返回 元类本身 ,否则返回 元类isa。这就是为什么获取 元类类方法 也可以得到 类方法的原因。

2. 关于 isKindOfClass

案例分析:

打印结果:

并且也在源码中找到 isMemberOfClassisKindOfClass 方法,也打上断点,来具体分析其原理:

但是执行过程中,却没有执行 isKindOfClass 方法,只执行了 isMemberOfClass 方法。先来分析 isMemberOfClass

isMemberOfClass

分析源码如下:

  • + isMemberOfClass: 是获取类的 元类 进行比较

  • - isMemberOfClass: 是获取 类对象 进行比较

分析上图代码:

  • re1NSObject 调用 + isMemberOfClass:NSObject 比较。因此 NSObject元类NSObject 并不相等,所以是0。
  • re3ZLObject 调用 + isMemberOfClass:ZLObject 比较。因此 ZLObject元类ZLObject 并不相等,所以是0。
  • re5NSObject对象 调用 - isMemberOfClass:NSObject 比较。因此 NSObject对象 的类是NSObject,与 NSObject 相等,所以是1。
  • re7ZLObject对象 调用 - isMemberOfClass:ZLObject 比较。因此 ZLObject对象 的类是ZLObject,与 ZLObject 相等,所以是1。

isKindOfClass

上述案例中,isKindOfClass 没有执行,只有 isMemberOfClass 相关方法执行了。这是什么原因呢?

具体分析汇编才知道,isKindOfClass没有执行的原因是底层执行了 objc_opt_isKindOfClass 方法。

具体分析 objc_opt_isKindOfClass

在上面的代码中,不管上层调用的是 + isKindOfClass: 还是 - isKindOfClass:,内部都会重定向到 objc_opt_isKindOfClass 这个方法。因为 其本质也是一个对象,我们称之为 类对象。所以obj每次都有值。

分析源码如下:

  • 如果是类:首先获取类的元类 比较。相等则返回true。如果不相等,再获取类的 父类 比较,相等则返回true。否则循环找 父类 比较,直到获取到的父类为 nil,依旧没有找到则返回false。

  • 如果是实例对象,首先获取 比较。相等则返回true。否则和 的步骤一样。

分析上图代码:

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

推荐阅读更多精彩内容