一、大概流程
两种方式:
- 快速方式
- 直接通过汇编在缓存里找到方法
- Objc_class中的
cache_t cache会存储方法的SEL和IMP,SEL和IMP会组成一张哈希表 - 为什么用汇编?
- C语言做不到通过写一个函数来实现保留未知参数跳转到任意的指针
- 快,C语言在底层仍需要编译
- 汇编有寄存器,可保留参数
- 慢速方式
- 当cache里没有该方法时就会经历一个缓慢切复杂的过程,如果在这个过程中找到了就会再存入cache
- 这个过程是通过C、C++和汇编一起完成的
二、源码跟踪
ENTRY _objc_msgSend
汇编部分:
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:// isa处理完毕后开始查找cache **重点**
CacheLookup NORMAL // calls imp or objc_msgSend_uncached
// 有缓存就调用,没缓存就走objc_msgSend_uncached
---------------------------------------------------------
/********************************************************************
*
* CacheLookup NORMAL|GETIMP|LOOKUP
*
* Locate the implementation for a selector in a class method cache.
*
* Takes:
* x1 = selector
* x16 = class to be searched
*
* Kills:
* x9,x10,x11,x12, x17
*
* On exit: (found) calls or returns IMP
* with x16 = class, x17 = IMP
* (not found) jumps to LCacheMiss
*
********************************************************************/
插曲 **LNilOrTagged**
#if SUPPORT_TAGGED_POINTERS
LNilOrTagged:
b.eq LReturnZero // nil check
// tagged
adrp x10, _objc_debug_taggedpointer_classes@PAGE
add x10, x10, _objc_debug_taggedpointer_classes@PAGEOFF
ubfx x11, x0, #60, #4
ldr x16, [x10, x11, LSL #3]
adrp x10, _OBJC_CLASS_$___NSUnrecognizedTaggedPointer@PAGE
add x10, x10, _OBJC_CLASS_$___NSUnrecognizedTaggedPointer@PAGEOFF
cmp x10, x16
b.ne LGetIsaDone // isa处理完毕
// ext tagged
adrp x10, _objc_debug_taggedpointer_ext_classes@PAGE
add x10, x10, _objc_debug_taggedpointer_ext_classes@PAGEOFF
ubfx x11, x0, #52, #8
ldr x16, [x10, x11, LSL #3]
b LGetIsaDone
// SUPPORT_TAGGED_POINTERS
#endif
** CacheLookup NORMAL **
.macro CacheLookup
// p1 = SEL, p16 = isa
省略
1: cmp p9, p1 // if (bucket->sel != _cmd)
b.ne 2f // scan more
CacheHit $0 // call or return imp -> TailCallCachedImp 直接调用
2: // not hit: p12 = not-hit bucket
CheckMiss $0 // miss if bucket->sel == 0
cmp p12, p10 // wrap if bucket == buckets
b.eq 3f
ldp p17, p9, [x12, #-BUCKET_SIZE]! // {imp, sel} = *--bucket
b 1b // loop
3: // wrap: p12 = first bucket, w11 = mask
add p12, p12, w11, UXTW #(1+PTRSHIFT)
// p12 = buckets + (mask << 1+PTRSHIFT)
省略
.macro CheckMiss
// miss if bucket->sel == 0
.if $0 == GETIMP
cbz p9, LGetImpMiss
.elseif $0 == NORMAL
cbz p9, __objc_msgSend_uncached // 因为之前传的NORMAL所以走这里
.elseif $0 == LOOKUP
cbz p9, __objc_msgLookup_uncached
.else
.abort oops
.endif
.endmacro
STATIC_ENTRY __objc_msgSend_uncached
UNWIND __objc_msgSend_uncached, FrameWithNoSaves
// THIS IS NOT A CALLABLE C FUNCTION
// Out-of-band p16 is the class to search
MethodTableLookup // 方法列表 -> 跳入__class_lookupMethodAndLoadCache3 此时进入C\C++部分
TailCallFunctionPointer x17 // 调用函数指针
END_ENTRY __objc_msgSend_uncached
STATIC_ENTRY __objc_msgLookup_uncached
UNWIND __objc_msgLookup_uncached, FrameWithNoSaves
// THIS IS NOT A CALLABLE C FUNCTION
// Out-of-band p16 is the class to search
MethodTableLookup
ret
C\C++部分:
IMP _class_lookupMethodAndLoadCache3(id obj, SEL sel, Class cls)
{
return lookUpImpOrForward(cls, sel, obj,
YES/*initialize*/, NO/*cache*/, YES/*resolver*/);
}
IMP lookUpImpOrForward(Class cls, SEL sel, id inst,
bool initialize, bool cache, bool resolver)
{
IMP imp = nil;
bool triedResolver = NO;
runtimeLock.assertUnlocked();
// Optimistic cache lookup
if (cache) //此处没有缓存,但如果别的方法进来有缓存的话也会走汇编,因为缓存就是为了快,所以用汇编
{
imp = cache_getImp(cls, sel);
if (imp) return imp;
}
runtimeLock.lock();
checkIsKnownClass(cls);// 判断当前类是否是已知的类
if (!cls->isRealized()) // 判断已知的类是否实现
{
realizeClass(cls); // DATA的一些列赋值
}
if (initialize && !cls->isInitialized()) {
runtimeLock.unlock();
_class_initialize (_class_getNonMetaClass(cls, inst));// 初始化类
runtimeLock.lock();
// If sel == initialize, _class_initialize will send +initialize and
// then the messenger will send +initialize again after this
// procedure finishes. Of course, if this is not being called
// from the messenger then it won't happen. 2778172
}
**重点开始**
retry:
runtimeLock.assertLocked();
// Try this class's cache.
// 设计的原因:并行的其他地方(比如重映射)可能已经将方法加入了cache
imp = cache_getImp(cls, sel);
if (imp) goto done;
// Try this class's method lists.
{
Method meth = getMethodNoSuper_nolock(cls, sel); // 从当前类找方法
if (meth) {
log_and_fill_cache(cls, meth->imp, sel, inst, cls); // 给cache赋值找到的方法
imp = meth->imp;
goto done;
}
}
// Try superclass caches and method lists.
{
unsigned attempts = unreasonableClassCount();
// 自己类里没找到方法,就开始循环找父类
for (Class curClass = cls->superclass;
curClass != nil;
curClass = curClass->superclass)
{
// Halt if there is a cycle in the superclass chain.
if (--attempts == 0) {
_objc_fatal("Memory corruption in class list.");
}
// Superclass cache.
// 检查一遍父类的缓存
imp = cache_getImp(curClass, sel);
if (imp) {
if (imp != (IMP)_objc_msgForward_impcache) {
// Found the method in a superclass. Cache it in this class.
log_and_fill_cache(cls, imp, sel, inst, curClass);
goto done;
}
else {
// Found a forward:: entry in a superclass.
// Stop searching, but don't cache yet; call method
// resolver for this class first.
break;
}
}
// Superclass method list.
// 缓存里没有就就在当前循环的父类中找方法
Method meth = getMethodNoSuper_nolock(curClass, sel);
if (meth) {
log_and_fill_cache(cls, meth->imp, sel, inst, curClass);
imp = meth->imp;
goto done;
}
}
}
↓-下-↓-一-↓-节-↓-的-↓-重-↓-点-↓
// No implementation found. Try method resolver once.
if (resolver && !triedResolver) {
runtimeLock.unlock();
_class_resolveMethod(cls, sel, inst);
runtimeLock.lock();
// Don't cache the result; we don't hold the lock so it may have
// changed already. Re-do the search from scratch instead.
triedResolver = YES;
goto retry;
}
↑-下-↑-一-↑-节-↑-的-↑-重-↑-点-↑
↓-下-↓-下-↓-一-↓-节-↓-的-↓-重-↓-点-↓
// No implementation found, and method resolver didn't help.
// Use forwarding.
imp = (IMP)_objc_msgForward_impcache;
cache_fill(cls, sel, imp, inst);
↑-下-↑-下-↑-一-↑-节-↑-的-↑-重-↑-点-↑
done:
runtimeLock.unlock();
return imp;
}
三、动态方法解析
如果自己类没有找到方法,循环父类也没有找到方法,就会动态方法解析
// No implementation found. Try method resolver once.只会走一次
if (resolver && !triedResolver) {
runtimeLock.unlock();
_class_resolveMethod(cls, sel, inst);
runtimeLock.lock();
// Don't cache the result; we don't hold the lock so it may have
// changed already. Re-do the search from scratch instead.
triedResolver = YES;
goto retry;
}
/***********************************************************************
* _class_resolveMethod
* Call +resolveClassMethod or +resolveInstanceMethod.
* Returns nothing; any result would be potentially out-of-date already.
* Does not check if the method already exists.
**********************************************************************/
void _class_resolveMethod(Class cls, SEL sel, id inst)
{
if (! cls->isMetaClass()) {// 判断是不是元类
// 目前看:传进来的是实例方法则走这里
// try [cls resolveInstanceMethod:sel]
_class_resolveInstanceMethod(cls, sel, inst);
}
else {
// 目前看:传进来的是类方法则走这里,因为类方法存在元类中,所以cls是元类,这点**很重要**
// try [nonMetaClass resolveClassMethod:sel]
// and [cls resolveInstanceMethod:sel]
_class_resolveClassMethod(cls, sel, inst);
if (!lookUpImpOrNil(cls, sel, inst,
NO/*initialize*/, YES/*cache*/, NO/*resolver*/))
{ // 如果lookUpImpOrNil找到了就不进来,进来说明resolveClassMethod没有进行补救
/* 这块不用看了,感觉写的迷迷糊糊的,直接看下面的双斜杠注释就行了
该方法会去找resolveInstanceMethod方法,会在NSObject里找到并调用
具体过程是:
cls是个元类,且要实现+resolveInstanceMethod方法
如果你在cls中写一个+resolveInstanceMethod方法
则在cls元类中只是一个相当于-resolveInstanceMethod方法的存在
所以不能在cls中写一个+resolveInstanceMethod方法
正确的方法是,把+resolveInstanceMethod方法加入NSObject中
这样cls的元类就能继承NSObject的方法
也就能给+resolveInstanceMethod方法发送消息了
*/
_class_resolveInstanceMethod(cls, sel, inst);
// 进去后会先判断cls的元类有没有存resolveInstanceMethod,而cls的元类就是根元类,因为NSObject实现了+resolveInstanceMethod,所以这里一定能找得到
}
}
}
-----------------------------------------------
resolve方法会调用两次
第一次是征程代码流程
第二次是在方法转发流程最后消息无法处理的时候系统又调了一次
不想让这里走两次就在这给一个方法
// 实例方法走这
+ (BOOL)resolveInstanceMethod:(SEL)sel
{
return [super resolveInstanceMethod:sel];
}
// 类方法走这
+ (BOOL)resolveClassMethod:(SEL)sel
{
return [super resolveClassMethod:sel];
}
static void _class_resolveInstanceMethod(Class cls, SEL sel, id inst)
{
// 判断当前传入的cls的元类中是否实现了SEL_resolveInstanceMethod
// 注意此处传进去的是**元类**,但是不会进行动态方法解析,因为NSObject实现了+resolveInstanceMethod方法
// 如果没有手动实现+ (BOOL)resolveClassMethod方法,就会一直找到NSObejct实现的+ (BOOL)resolveClassMethod方法,而能找到NSObject的+ (BOOL)resolveClassMethod方法的原因是:NSObject所实现的+ (BOOL)resolveClassMethod方法存在根元类里
// NSObject中有resolveInstance方法且返回NO,所以如此处也不会return掉
if (! lookUpImpOrNil(cls->ISA(), SEL_resolveInstanceMethod, cls,
NO/*initialize*/, YES/*cache*/, NO/*resolver*/))
{
// Resolver not implemented.
return;
}
// 发送消息
BOOL (*msg)(Class, SEL, SEL) = (typeof(msg))objc_msgSend;
bool resolved = msg(cls, SEL_resolveInstanceMethod, sel);
// Cache the result (good or bad) so the resolver doesn't fire next time.
// +resolveInstanceMethod adds to self a.k.a. cls
// 如果在SEL_resolveInstanceMethod中添加了要查找的方法,则此处会再查一次
IMP imp = lookUpImpOrNil(cls, sel, inst,
NO/*initialize*/, YES/*cache*/, NO/*resolver*/);
if (resolved && PrintResolving) {
省略
}
}
static void _class_resolveClassMethod(Class cls, SEL sel, id inst)
{
assert(cls->isMetaClass());
if (! lookUpImpOrNil(cls, SEL_resolveClassMethod, inst,
NO/*initialize*/, YES/*cache*/, NO/*resolver*/))
{
// Resolver not implemented.
return;
}
// 发送给类对象
BOOL (*msg)(Class, SEL, SEL) = (typeof(msg))objc_msgSend;
bool resolved = msg(_class_getNonMetaClass(cls, inst),
SEL_resolveClassMethod, sel);
// Cache the result (good or bad) so the resolver doesn't fire next time.
// +resolveClassMethod adds to self->ISA() a.k.a. cls
IMP imp = lookUpImpOrNil(cls, sel, inst,
NO/*initialize*/, YES/*cache*/, NO/*resolver*/);
if (resolved && PrintResolving) {
省略
}
}
四、消息转发
#pragma mark - 消息转发
- (id)forwardingTargetForSelector:(SEL)aSelector{
NSLog(@"%s",__func__);
return [super forwardingTargetForSelector:aSelector];
}
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector{
NSLog(@"%s",__func__);
return [super methodSignatureForSelector:aSelector];
}
- (void)forwardInvocation:(NSInvocation *)anInvocation{
[anInvocation invoke];
}
#pragma mark - 类消息转发
// 只有汇编调用 没有源码实现
-------------------------
STATIC_ENTRY __objc_msgForward_impcache
// Method cache version
// THIS IS NOT A CALLABLE C FUNCTION
// Out-of-band Z is 0 (EQ) for normal, 1 (NE) for stret
beq __objc_msgForward
b __objc_msgForward_stret
END_ENTRY __objc_msgForward_impcache
-------------------------
+ (id)forwardingTargetForSelector:(SEL)aSelector{
return [super forwardingTargetForSelector:aSelector];
}
//
+ (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector{
NSLog(@"%s",__func__);
return [super methodSignatureForSelector:aSelector];
}
+ (void)forwardInvocation:(NSInvocation *)anInvocation{
NSLog(@"%s",__func__);
[anInvocation invoke];
}