OC底层原理(9)- runtime简介,方法的本质(随记)

431 阅读3分钟

一、runtime

1、概述:由 c、c++、汇编混编写成的为OC提供运行时功能的一套api。

运行时:将代码加载到内存里面

编译时:编译时将语法翻译成机器可以识别的语言

2、runtime 应用方式:

1)Objective-C code 方式调起一些方法,如, @selector()

  1. NSObject 的方法 如,NSSelectorFromString()

3)sel_registerName 函数api

二、方法的本质

为了探索方法的本质主要是探索我们在调用一个方法的过程,到底做了什么,那么我们先将main.m clang 一下,生成cpp 文件并打开

然后在cpp文件底部main 方法中可以看到主要是调用了两个方法,alloc 与 sayHello,跟在.m 中一样

在调用方法的时候,有看到都有一个类型强转 “(LGPerson ()(id, SEL))(void *)” ,要是把这一部分删掉就变成了

这时看起就很像 runtime 用法之一的 @selector 形式

于是就可以说,方法的本质就是通过 objc_msgSend 来发送消息的

发送消息方法里有两个参数 id:消息接受者,sel:方法编号,就可以去找imp

然后定义一个函数并调用,如下图run 会不会也去发送消息呢?

继续clang一下cpp,并打开

这是我们看到的就是直接 run(); 这是为什么呢?

这是因为我们OC调用方法的过程就是通过发送消息去找函数实现的过程,只不过在底层封装了这一过程。所以如果我们直接就调用函数,就可以通过函数名这个指针对标到这一函数。

发送消息:objc_msgSend 对象方法(对象, sel); 类方法(类对象, sel); 父类:objc_msgSendSupper

发送方法的几种情况

通过以上分析,我们知道了,方法本质不可或缺的就是objc_msgSend,然后接下来就开始对 objc_msgSend 探索

三、objc_msgSend 探索

在汇编环境下,找到objc_msgsend,再点进去

就可以看到 objc_msgSend 源码在哪个库里

objc_msgSend 源码获取地址:

接下来就要看汇编,因为 objc_msgSend 是用汇编实现的

为什么要用到汇编呢?因为性能:1)汇编更加容易被机器识别,2)还有时候会有参数类型与个数不确定的时候,在C语言中不可能通过写一个函数来保留未知的参数并且跳转到一个任意的函数指针,C语言没有满足做这件事情的必要特性。

消息查找机制:1)快速流程-接下来;2)慢速-后面讲

直接看 arm64 下的,因为我们现在用的最多的,那么arm64 有多少个通用寄存器呢?31位寄存器。x0-x7,存储参数,另外X0也是返回

接下来就不会汇编做具体分析,汇编对我超纲了,直接贴出objc_msgSend作用流程:

1)ENTRY _objc_msgSend

2)对消息接受者(id self, sel_cmd)判断处理

3)taggerPointer 判断处理

4)‘GetClassFromIsa_p16 isa’指针处理

5)CacheLookup 查找缓存

6)‘cache_t’处理‘bucket’以及内存哈希处理

a、找不到递归下一个‘bucket’

b、找到了就返回 '{imp,sel} = *bucket->imp'

c、遇到意外就重试

d、找不到就 ‘JumpMiss’

7)_objc_msgSend_uncached 告诉找不到缓存 ‘imp’

8)‘STATIC_ENTRY _objc_msgSend_uncache’

9)‘MethodTableLookup’ 方法表查找

a、‘save parameter registers’

b、‘sel’以及_cmd 准备

c、‘_class_lookupMethodAndLoadCache3’调用