iOS-方法的本质objc_msgSend

581 阅读2分钟

1:基础知识储备。

Runtime:一套API,集合C C++ 汇编语言而成,提供运行时功能。

当前OC的两个版本,legency and Modern

Runtime使用方式,1:OCcode: @selector 2:NSObject方法:NSSelectorFrom 3:sel_registerName 函数API。

2:方法本质的探索

clang 转译的代码中发现方法转为,objc_msgSend;断点查看汇编的代码,调用的方法之后也是objc_msgSend

全局搜索查看objc_msgSend,发现其使用汇编写的,C语言中不可能通过写一个函数来保留未知的参数并且跳转到一个任意的函数指针。C语言无法满足这件事的必要性;objc_msgSend必须要足够快,汇编语言最接近机器语言。

objc_msgSend中有一个快速路径(汇编语言) 还有一个慢速路径(C语言写的)。

objc-msg-arm64.s

ENTRY _objc_msgSend
	UNWIND _objc_msgSend, NoFrame

	cmp	p0, #0			// nil check and tagged pointer check
#if SUPPORT_TAGGED_POINTERS
	b.le	LNilOrTagged		//  (MSB tagged pointer looks negative)
#else
	b.eq	LReturnZero
#endif
	ldr	p13, [x0]		// p13 = isa
	GetClassFromIsa_p16 p13		// p16 = class
LGetIsaDone:
	CacheLookup NORMAL		// calls imp or objc_msgSend_uncached

#if SUPPORT_TAGGED_POINTERS
LNilOrTagged:
	b.eq	LReturnZero		// nil check
	
	......(只展示部分代码。。。)

快速路径,汇编语言大体流程:

1:ENTRY _objc_msgSend

2:tagged pointer 判断

3: 获取对象的isa ,并且GetClassFromIsa_p16(根据isa获取Class,isa & ISA_MASK).

4 : CacheLookup过程查找缓存方法。(CacheLookup NORMAL // calls imp or objc_msgSend_uncached)。 
通过指针偏移找到 cache_t,buckets找到相应的 imp 返回, 找不到 JumpMiss 。

5 : JumpMiss Normal-> __objc_msgSend_uncached -> MethodTableLookup

6 : 调用 __class_lookupMethodAndLoadCache3(C 函数), 来到慢速查找流程。

__class_lookupMethodAndLoadCache3是C语言的函数调换用,来到了慢速查找以及消息转发的阶段。

IMP _class_lookupMethodAndLoadCache3(id obj, SEL sel, Class cls)
{
    return lookUpImpOrForward(cls, sel, obj, 
                              YES/*initialize*/, NO/*cache*/, YES/*resolver*/);
}

Conclusion

方法查找的本质是 在编译期llvmOC方法编译成了objc_msgSend等函数,通过sel快速缓存查找到imp,找到则返回imp,否则进行慢速查找以及消息转发机制。