iOS底层开发消息发送与转发流程
Posted WeaterMr
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了iOS底层开发消息发送与转发流程相关的知识,希望对你有一定的参考价值。
ios底层开发消息转发流程
一,cache缓存读取流程分析
首先我们上一章已经了解到对应的cache_t的数据结构
_bucketsAndMaybeMask
:指针类型,存放buckets的首地址
_maybeMask
:当前的缓存区count
_flags
:同_occupied
_occupied
:当前cache的可存储的buckets数量,默认是0
_originalPreoptCache
:初始时候的缓存(注意联合体互斥)
下面我们主要分析这些成员变量的主要作用。
首先我们要了解到一点这里buckets的存储空间是一块连续的内存。
当我们读取 cache
是主要是通过,_bucketsAndMaybeMask
做内存平移取对应buckets中的数据,在我们插入是通过_occupied
判断什么时候扩容,通过_maybeMask
来处理什么时候读取数据结束。这里为什么不把所有的数据都放到当前的结构体中呢?这就好比你做为一个仓库管理员,不把所有的物品放到你自己办公室一样,你只是把所有仓库的的钥匙放到你的抽屉中,上面会标记当前仓库的位置(_bucketsAndMaybeMask
),空间有多大(_maybeMask
),已经使用了多大空间(_occupied
)。
二,编译时与运行时概念
编译时
顾名思义就是正在编译的时候 ,就是编译器帮你把源代码翻译成机器能识别的代码。就好比当一个汽车生产完成之后,会做一个系统的巡检,看看轮胎是否有气,螺丝是否上好,编译时即:检查你的代码是否有错误,能不能跑起啦!
运行时
就是代码跑起来了.被装载到内存中去了。检查没有问题,加油,上高速。程序被执行了,不在是简单的类型扫描和静态分析,而是在内存中做些操作。
计算机科学领域的任何问题都可以通过增加一个间接层来解决
计算机系统软件体系结构的设计要点,整个体系结构从上到下都是按照严格的层次结构设计的。
层与层之间的通讯,遵循相关的通信协议,也可以称之为接口
。这样做的目的尽量保证整体的稳定性,可扩展性,中间层都是对下一层的包装和扩展。
当我们的objective-C 或framework 在调用一些方法时,其实是通过调用 runtime api来进行与底层进行通讯的。 其中一个重要的环节是通过编译器进行转换才能调用底层的方法。
将对应的oc文件编译成.cpp文件
int main(int argc, const char * argv[]) {
/* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
Goods *good = ((Goods *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("Goods"), sel_registerName("alloc"));
((void (*)(id, SEL))(void *)objc_msgSend)((id)person, sel_registerName("number"));
((id (*)(id, SEL, SEL))(void *)objc_msgSend)((id)person, sel_registerName("performSelector:"), sel_registerName("number"));
}
return 0;
}
对象方法的调用本质为,消息的转发,事件发生在运行时
。
消息的转发包含两个主要参数:
((id)objc_getClass("Goods")
-->receiver
(消息的接收者)
sel_registerName("alloc"));
-->另一个是SEL
(方法名称)
三,通过代码验证消息转发流程
提示
xcode设置:target -> Build Settings 搜索-- Enable Strict Checking of objc_msgSend calls 设置为NO
#import <objc/message.h>
int main(int argc, const char * argv[]) {
@autoreleasepool {
// 0x00007ffffffffff8ULL
// class_data_bits_t
GoodTwo *p = [GoodTwo alloc];
[p saySomething];
objc_msgSend(p, sel_registerName("saySomething"));
}
return 0;
}
-[GoodTwo saySomething]
(int) argc = 1
(const char **) argv = 0x00007ffeefbff4c8
(GoodTwo *) p = 0x000000010140afa0
-[GoodTwo saySomething]
我们可以看到通过[p saySomething]
等同于 objc_msgSend(p, sel_registerName("saySomething"));
即,方法调用的本质是消息的发送与转发。
也即是所有的上层代码通过编译器,在底层都会有一个解释。
四,objc_msgSend的汇编实现
ENTRY _objc_msgSend
UNWIND _objc_msgSend, NoFrame
1. cmp p0, #0 // nil check and tagged pointer check
2. #if SUPPORT_TAGGED_POINTERS
b.le LNilOrTagged // (MSB tagged pointer looks negative)
#else
b.eq LReturnZero
#endif
3. ldr p13, [x0] // p13 = isa
4. GetClassFromIsa_p16 p13, 1, x0 // p16 = class 这是一个宏定义,5.在下面方法中
LGetIsaDone:
// calls imp or objc_msgSend_uncached
CacheLookup NORMAL, _objc_msgSend, __objc_msgSend_uncached
#if SUPPORT_TAGGED_POINTERS
LNilOrTagged:
b.eq LReturnZero // nil check
GetTaggedClass
b LGetIsaDone
// SUPPORT_TAGGED_POINTERS
#endif
5. LReturnZero:
// x0 is already zero
mov x1, #0
movi d0, #0
movi d1, #0
movi d2, #0
movi d3, #0
ret
END_ENTRY _objc_msgSend
.macro GetClassFromIsa_p16 src, needs_auth, auth_address /* note: auth_address is not required if !needs_auth */
#if SUPPORT_INDEXED_ISA
// Indexed isa
mov p16, \\src // optimistically set dst = src
tbz p16, #ISA_INDEX_IS_NPI_BIT, 1f // done if not non-pointer isa
// isa in p16 is indexed
adrp x10, _objc_indexed_classes@PAGE
add x10, x10, _objc_indexed_classes@PAGEOFF
ubfx p16, p16, #ISA_INDEX_SHIFT, #ISA_INDEX_BITS // extract index
ldr p16, [x10, p16, UXTP #PTRSHIFT] // load class from array
1:
#elif __LP64__
.if \\needs_auth == 0 // _cache_getImp takes an authed class already
mov p16, \\src
.else
// 64-bit packed isa
ExtractISA p16, \\src, \\auth_address
.endif
#else
// 32-bit raw isa
mov p16, \\src
#endif
.endmacro
.macro ExtractISA
and $0, $1, #ISA_MASK
- 1:
cmp
p0
,#0
p0
为消息接收者receiver,
判断当前的接受者是否为空。 - 2:判断是不是
SUPPORT_TAGGED_POINTERS
类型,意思是不是tagged pointers
指针如果是,执行b.le LNilOrTagged,
然后在里面执行b.eq LReturnZero
。如果不是SUPPORT_TAGGED_POINTERS
类型,直接b.eq LReturnZero
,此次objc_msgSend无效,结束本次消息发送。 - 3:如果
p0
存在,则将x0
存入到p13
,x0
是receiver
,即类的首地址isa。 - 4:进入GetClassFromIsa_p16,传递的参数
src=p13
、needs_auth=1
、auth_address=x0
,由于_need_auth=1
,进入分支ExtractISA p16, \\src, \\auth_address,
此ExtractISA为宏,操作是将\\src(isa)、#ISA_MASK做与操作,得到了Class,结果存入到p16中。 - 5:
LGetIsaDone
:获取isa完成。接下来执行CacheLookup NORMAL, _objc_msgSend, __objc_msgSend_uncached:
未完待续。。。
以上是关于iOS底层开发消息发送与转发流程的主要内容,如果未能解决你的问题,请参考以下文章