AI智能
改变未来

iOS OC 方法的本质

iOS OC 方法的本质

  • 前言:
  • 1. 方法本质初探
  • 2. `objc_msgSend`汇编分析

前言:

前面探究了方法在类中的缓存,那么方法的本质是什么呢?方法调用在底层做了什么呢?今天我们来探索一下:

1. 方法本质初探

看一下一段代码:
先定义一个

LGPerson

类,然后定义

sayNB

对象方法,然后在

main

函数中调用

int main(int argc, const char * argv[]) {@autoreleasepool {LGPerson *person = [LGPerson alloc];[person sayNB];}return 0;}

然后通过

clang

生成

cpp

文件,在底层编译的

cpp

文件中查看

main

函数如下:

int main(int argc, const char * argv[]) {/* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;LGPerson *person = ((LGPerson *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass(\"LGPerson\"), sel_registerName(\"alloc\"));((void (*)(id, SEL))(void *)objc_msgSend)((id)person, sel_registerName(\"sayNB\"));imp -  函数}return 0;}

由此:我们可以简单得出,

方法

的本质是通过

objc_msgSend

发送消息,第一个参数为

id

消息接受者,第二个参数为

sel

方法编号。

那么我们定义的函数会调用

objc_msgSend

发送消息吗?
我们定义下面函数,并在

main

中调用,

void run(){NSLog(@\"%s\",__func__);}

通过

clang

查看

cpp

文件,发现函数不需要调用

objc_msgSend

,函数可以直接通过

函数名

(指针),找到函数的实现,不需要像

方法

通过

sel

,找到

ipm

,再找到方法的实现。

父类

发送消息(对象方法):

struct objc_super lgSuper;lgSuper.receiver = s;lgSuper.super_class = [LGPerson class];objc_msgSendSuper(&lgSuper, @selector(sayHello));

父类

发送消息(类方法):

struct objc_super myClassSuper;myClassSuper.receiver = [s class];myClassSuper.super_class = class_getSuperclass(object_getClass([s class]));// 元类objc_msgSendSuper(&myClassSuper, sel_registerName(\"sayNB\"));

objc_super

源码:

struct objc_super {/// Specifies an instance of a class.__unsafe_unretained _Nonnull id receiver;/// Specifies the particular superclass of the instance to message.#if !defined(__cplusplus)  &&  !__OBJC2__/* For compatibility with old objc-runtime.h header */__unsafe_unretained _Nonnull Class class;#else__unsafe_unretained _Nonnull Class super_class;#endif/* super_class is the first class to search */};#endif

因此,在调用

Runtim api

父类

发送消息时,需要设置

receiver

super_class

问题:在测试中,不要严格识别参数,需要如下设置:

2.

objc_msgSend

汇编分析

objc

源码中断点

然后

Debug -> Debug Workflow ->always Show Disassembly

进行汇编分析:

objc_msgSend

处断点,

通过

control + in

,查看

libobjc.A.dylib objc_msgSend

,发现

objc_msgSend

底层是用

汇编

实现的。

补充:

为什么`objc_msgSend`用汇编实现呢?1. 在性能方面,`汇编`更容易被机器识别2. 在发送消息时,有很多未知的参数,c 语言中不能通过写一个函数来保留未知的参数并且跳转到一个任意的函数指针,c语言没有满足做这件事的必要特性。汇编寄存器arm64下有31位通用寄存器,x0 - x7,是参数,返回值会放到 x0中

objc_msgSend

的汇编分析:

首先,在

objc

源码中全局搜索

objc_msgSend

找到汇编源码,


汇编代码:

具体分析:

1. cmp	p0, #0			// nil check and tagged pointer check先对比当前0号寄存器是否为空,为空,当前没有接收者2. 判断 SUPPORT_TAGGED_POINTERS直接执行 LNilOrTagged 或者 LReturnZero3. 当有消息接收者,正常情况下,拿到 p13	// p13 = isa4. GetClassFromIsa_p16 p13	 通过p13(isa),获取 Class ;GetClassFromIsa_p16 先平移,取值 shiftcls,然后得到 Class, 或者 isa & Mask 直接获取 Class

LGetIsaDone

源码:

5. LGetIsaDone 查找isa完毕  开始正常查找 CacheLookup NORMAL5.1  ldp	p10, p11, [x16, #CACHE]	// p10 = buckets, p11 = occupied|mask先平移16字节,获得cache,找到缓存方法的 buckets 和 occupied5.2 and	w12, w1, w11		// x12 = _cmd & mask通过 _cmd & mask 获取哈希的下标,5.3 循环查找 bucket  add	p12, p10, p12, LSL5.4 ldp p17, p9, [x12] 通过 sel 找到 bucket 中的cmd 对比,相等直接返回 CacheHit $0, 找不到,直接走 b.ne	2f 即:CheckMisscmp	p9, p1			// if (bucket->sel != _cmd)6. CheckMiss 中找到后,b.eq 3f,进入步骤三,平移哈希,将方法缓存到 bucket中一份,如果没有找到则 {imp, sel} = *--bucket,循环递归查找。然后会在查找一遍 5.4 流程(防止多线程,缓存更新),找不到缓存,则 JumpMiss $0

CheckMiss

代码:


当时

NORMAL

形式时,进入

__objc_msgSend_uncached

,如下:

MethodTableLookup

源码:

_class_lookupMethodAndLoadCache3

方法:

IMP _class_lookupMethodAndLoadCache3(id obj, SEL sel, Class cls){return lookUpImpOrForward(cls, sel, obj,YES/*initialize*/, NO/*cache*/, YES/*resolver*/);}
问题:为什么从汇编调用 C 方法?_class_lookupMethodAndLoadCache3 是一系列慢速方法查找,没有必要使用汇编

总结:

1. ENTRY _objc_msgSend 进入2. TAGGED_POINTERS  判断3. GetClassFromIsa_p16 p13 通过 isa 获取 Class4. 缓存查找 CacheLookup5. cache_t 处理,处理哈希,查找 buckrt,找到返回{imp,sel} = *buckrt->imp,找不到 JumpMiss6. 缓存中找不到方法 进入 __objc_msgSend_uncached7. STATIC_ENTRY __objc_msgSend_uncached8. MethodTableLookup  调用__class_lookupMethodAndLoadCache3

最后一个遗留问题,调用

_class_lookupMethodAndLoadCache3

中是怎么慢速查找的呢?下一篇接着探索。

  • 点赞1
  • 收藏
  • 分享
  • 文章举报

亮亮不想说话发布了18 篇原创文章 · 获赞 10 · 访问量 266私信关注

赞(0) 打赏
未经允许不得转载:爱站程序员基地 » iOS OC 方法的本质