objc_msgSend

390 阅读7分钟

一、大概流程

两种方式:

  1. 快速方式
    • 直接通过汇编在缓存里找到方法
    • Objc_class中的cache_t cache会存储方法的SEL和IMP,SEL和IMP会组成一张哈希表
    • 为什么用汇编?
      1. C语言做不到通过写一个函数来实现保留未知参数跳转到任意的指针
      2. 快,C语言在底层仍需要编译
      3. 汇编有寄存器,可保留参数
  2. 慢速方式
    • 当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];
}