objc_msgSend

OC中的调用方法都是转换为objc_msgSend函数调用,调用objc_msgSend函数的方式是:

objc_msgSend(消息接受者receiver, 方法名SEL, 参数params(可不传也可多个参数))

了解了objc_msgSend调用与传参后,我们来看看苹果是如何实现objc_msgSend函数的,objc_msgSend函数实现为汇编语言,他在objc-msg-arm64.s文件中

// _objc_msgSend的开始
ENTRY _objc_msgSend
	UNWIND _objc_msgSend, NoFrame

// 检查消息接受者是否为nil
	cmp	p0, #0			// nil check and tagged pointer check
#if SUPPORT_TAGGED_POINTERS  //支持tagged point
	// 小于等于0 跳转到 LNilOrTagged
	b.le	LNilOrTagged		//  (MSB tagged pointer looks negative)
#else
	// 等于0 跳转到LReturnZero
	b.eq	LReturnZero
#endif
	// 将isa地址存入寄存器 p13
	ldr	p13, [x0]		// p13 = isa
	// 调用宏GetClassFromIsa_p16,传入 isa, 1 , 消息接受者地址
	GetClassFromIsa_p16 p13, 1, x0	// p16 = class
LGetIsaDone:
	// calls imp or objc_msgSend_uncached
	// 调用CacheLookup,并传入 NORMAL, _objc_msgSend, __objc_msgSend_uncached
	CacheLookup NORMAL, _objc_msgSend, __objc_msgSend_uncached

#if SUPPORT_TAGGED_POINTERS
LNilOrTagged:
	// 消息接受者为nil,跳转到LReturnZero
	b.eq	LReturnZero		// nil check
	// 得到tagged point 的class
	GetTaggedClass
	// 调用LGetIsaDone
	b	LGetIsaDone
// SUPPORT_TAGGED_POINTERS
#endif
// 返回 nil
LReturnZero:
	// x0 is already zero
	mov	x1, #0
	movi	d0, #0
	movi	d1, #0
	movi	d2, #0
	movi	d3, #0
	ret
	// _objc_msgSend 的开始
	END_ENTRY _objc_msgSend

总结objc_msgSend

  1. 判断消息这是否为nil,判断时需要区分是否是tagged pointer
  2. 根据isa得到class
  3. 调用CacheLookup

CacheLookup

Tips:如果学过cache_t::insert函数实现逻辑,那么理解CacheLookup会更加容易。

展示的源码去掉了非iOS真机的部分,只保留了iOS arm64的代码,CacheLookup可以分为三个部分来讲,

  1. 找到cache_t
  2. 找到buckets散列表
  3. 计算当前sel对应的下标index