OC底层原理十二: objc_msgSend(方法快速查找)

OC底层原理 学习大纲

上一节已了解类的cache结构插入操作。但是有几个问题:

  • 1. 何时插入缓存?
  • 2. 缓存读取机制是怎样?
cache分析流程

现在开始探索之旅

1. 探索插入操作
2. 介绍Runtime
3. 了解方法的本质
4. objc_msgSend解析

1. 探索插入操作

我们从insert开始寻找谁在调用它

  • objc4源码下搜索->insert(c++的调用方式->
image.png
  • 发现cache_fill调用了它,我们继续搜索cache_fill:
image.png

我们发现,在缓存写入之前,我们需要先知道:

  • 给谁进行写入objc_msgSend
  • 写入内容是什么cache_getImp

到这里,我们必须引出OC非常重要的机制:Runtime运行时

2. 介绍Runtime

👉 Objective-C Runtime Programming Guide,此文档不再更新,适合初步了解Runtime

2.1 什么是Runtime

  • Runtime是一个由CC++汇编混合开发的API库,它将程序的一些决定性工作编译器推迟到运行期,使得OC语言具备动态特性。内部使用消息机制进行通信

2.2 什么是运行时? 什么是编译时?

  • 编译时:编译器将源代码翻译成机器识别代码。这是一个静态操作,并不会把代码写入内存中进行运行
  • 编译过程中,会分析语法是否正确。
  • 编译时提示的errorwarning都是编译时错误
  • 编译过程检查就叫编译时类型检查静态类型检查
  • 运行时: 将代码装载内存中,让代码运行起来
  • 代码在装载内存之前,只是个"死家伙",静静地趴在磁盘中。只有载入内存,才是"活的"
  • 运行时类型检查与前面所说的编译时类型检查(或叫静态类型检查)不一样,它不是简单的扫描代码,而是在内存中做些操作,做些判断。(是动态活动的)

例如一个函数,只声明未实现command+B编译时不会报错,但是command+R运行时会报错。

image.png

2.3 Runtime版本

  • Runtime有两个版本, Legacy(早期版本) 和Modern(现行版本)
  • 早期版本: Objective-C 1.0,用于32位Mac OS X的平台上,实例变量发生改变后,需要重新编译其子类
  • 现行版本: Objective-C 2.0,用于iPhone程序和Mac OS X v10.5以后的系统中的 64 位程序实例变量发生改变后,不需要重新编译其子类

2.4 运行时让OC具备多态特性

  • OC的运行时机制:将数据类型的确定由编译时,推迟到运行时。OC的这种运行时机制使对象的类型对象的属性方法运行时才能确定

  • 多态: 不同对象以自己的方式响应相同的消息的能力叫做多态

例如:
自然界中的人类(Person)都有一个相同的方法-sing,男人(Man)类属于人类,女人(Wonan)类也属于人类,都继承了人类后,会实现各自的-sing方法。但是自然界中男人和女人的sing风格又不一样,男人唱的豪迈女人唱的委婉,但都继承了person唱的能力,这就是多态的现象。
也就是不同的对象以自己的方式响应相同消息的能力叫多态。 也可以说运行时机制是多态基础
描述参考👉: 码上江湖

2.5 Runtime的三种调用

  • oc代码调用、framework调用 、RuntimeAPI调用


    image.png

2.6 探索runtime

先准备好静态资源的,咱们才能动态起来。所以先获取compiler层文件。

  • 测试代码:
@interface HTPerson : NSObject
- (void)sayHello;
@end

@implementation HTPerson
- (void)sayHello{
    NSLog(@"%s",__func__);
}
@end

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

        HTPerson *p  = [HTPerson alloc];
        [p sayHello];
    }
    return 0;
}
  • clang静态编译main.m文件: clang -rewrite-objc main.m -o main.cpp
image.png

我们发现,所有OC方法,不管是类方法alloc,还是实例方法sayHello都是调用了objc_msgSend发送消息。

  • 调用格式: objc_msgSend: (消息接收者, 消息主体)

  • 尝试手动使用objc_msgSend执行方法:

  1. 导入头文件#import <objc/message.h>
  2. 手动关闭运行时的编译警告: buildSetiing->Enable Strict Checking of objc_msgSend Calls->设置为No
  3. 加入测试代码objc_msgSend(p, sel_registerName("sayHello"));
  4. 打印查看:
image.png

崩溃暂时不理会,我们现在发现sayHello打印成功了

3. 了解方法的本质

我们知道,方法的本质就是一个方法名和对应的函数代码

  • OC中,我们使用执行对象+函数名进行函数调用 (例如:[person sayHello])。

  • 内部完整的调用流程是: objc_msgSend发送消息(class sel) -> 通过sel(方法编号)找到imp(函数指针地址) -> 找到函数内容

  • 第一步: 发送消息我们有三种API调用方法(oc代码调用、framework调用 、RuntimeAPI调用)
  • 第三步:可直接 从函数指针地址读取函数内容

上述流程,我们唯一不知道的就是系统如何通过sel(方法编号)找到imp(函数指针地址)?

4. objc_msgSend解析

了解sel如何找到imp就是探究 objc_msgSend内部机制。

  • 函数的调用是极其频繁的,所以对性能的要求非常高。objc_msgSend使用汇编进行编写

  • imp的查找分为2个阶段,快速查找(缓存cache中,汇编编写)和慢速查找(方法列表methodTable中,c和c++编写),今天先介绍快速查找

接下来的知识,需要大家先熟悉cache_t的结构

汇编查找函数的流程: 从指定类开始->定位cache->定位buckets->哈希运算获取首次位置->循环寻找位置->返回impnull

  • 打开objc4源码,搜索objc_msgsend,我们选择arm64真机环境进行探索(其他环境也是类似逻辑)。

  • 找到objc_msgSend入口

image.png
  • 初始化数据, 从receiver中读取isa中的

    image.png

  • 当前objc-msg-arm64.s汇编文件中搜索CacheLookup,找到.macro CacheLookup定义处:

image.png
  • 如果匹配sel成功,调用Cachehit命中缓存流程, 返回找到的imp
image.png
  • 如果匹配失败,触发CheckMissJumpMiss流程, 告知外部Cache未找到imp

    image.png

  • 未找到imp时,进入__objc_msgSend_uncached流程,搜索__objc_msgSend_uncached

image.png
  • 发现缓存找不到后,进入方法列表去查找。 搜索MethodTableLookup
image.png
  • 跳转_lookUpImpOrForward,进入慢速查找阶段。

奉上完整流程

image.png

下一节: OC底层原理十三: objc_msgSend(方法慢速查找)

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