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,x0receiver,即类的首地址isa。
  • 4:进入GetClassFromIsa_p16,传递的参数src=p13needs_auth=1auth_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:

未完待续。。。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值