美文网首页
objc_msgSend 源码跟踪

objc_msgSend 源码跟踪

作者: forping | 来源:发表于2020-12-15 16:59 被阅读0次

    在Object-c中,OC方法的调用在编译时都会转化为对函数objc_msgSend的调用。

    举个例子

    [AppDelegate alloc];
    // ((AppDelegate *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("AppDelegate"), sel_registerName("alloc"));
    objc_msgSend(objc_getClass("AppDelegate"), sel_registerName("alloc"));
    
    [view1 addSubview:view2];
    // ((void (*)(id, SEL, UIView * _Nonnull))(void *)objc_msgSend)((id)view1, sel_registerName("addSubview:"), (UIView *)view2);
    objc_msgSend(view1, sel_registerName("addSubview:"), view2);
    

    本文章贴出的代码出自 objc4-781/
    下载地址下载地址

    objc_msgSend函数

    objc_msgSend函数是OC方法调用的核心,负责查找方法的实现,并执行方法函数。因调用频率非常高,函数的内部代码是用汇编语言编写,并且没有涉及需要线程同步和锁相关的代码。
    简单的宏定义

    ;定义一个名字叫做 ENTRY 的宏
    .macro ENTRY /* name */
        .text
        .align 5
        .globl    $0
    $0:
    .endmacro
    ;定义一个名字叫做 END_ENTRY 的宏
    .macro END_ENTRY /* name */
    LExit$0:
    .endmacro
    

    _objc_msgSend

        ENTRY _objc_msgSend
        UNWIND _objc_msgSend, NoFrame
    // p0是objc_msgSend的第一个参数  obj,p0 - 0 结果放到cpsr寄存器里
        cmp p0, #0          // nil check and tagged pointer check  
    // 如果支持 tagged_pointers
    #if SUPPORT_TAGGED_POINTERS
    // le 小于等于,如果第一个参数小于等于0 走 LNilOrTagged 流程
        b.le    LNilOrTagged        //  (MSB tagged pointer looks negative)
    #else
    //  eq 等于, 直接返回 0 
        b.eq    LReturnZero
    #endif
    // isa 存储到 p13寄存器,再存储到p16寄存器
        ldr p13, [x0]       // p13 = isa
        GetClassFromIsa_p16 p13     // p16 = class
    LGetIsaDone:
        // calls imp or objc_msgSend_uncached
    // 查找缓存,调用方法
        CacheLookup NORMAL, _objc_msgSend
    
    #if SUPPORT_TAGGED_POINTERS
    LNilOrTagged:
        b.eq    LReturnZero     // nil check
        GetTaggedClass
        b   LGetIsaDone
    // SUPPORT_TAGGED_POINTERS
    #endif
    
    LReturnZero:
        // x0 is already zero
        mov x1, #0 
        movi    d0, #0
        movi    d1, #0
        movi    d2, #0
        movi    d3, #0
        ret ;直接返回0
    
        END_ENTRY _objc_msgSend
    

    CacheLookup宏定义

    .macro CacheLookup
    
        //   NORMAL and LOOKUP:
        //   - x0 contains the receiver
        //   - x1 contains the selector
        //   - x16 contains the isa
        //   - other registers are set as per calling conventions
    LLookupStart$1:
        // p1 = SEL, p16 = isa
        ldr p11, [x16, #CACHE]              // p11 = mask|buckets  通过x16存储的isa 加上cache的地址偏移,或者cache的地址,存储到 p11 寄存器
    
    #if CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_HIGH_16
        and p10, p11, #0x0000ffffffffffff   // p10 = buckets 找到bucket 线性表
        and p12, p1, p11, LSR #48       // x12 = _cmd & mask  哈希计算,得到哈希值
    #elif CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_LOW_4
        and p10, p11, #~0xf         // p10 = buckets
        and p11, p11, #0xf          // p11 = maskShift
        mov p12, #0xffff
        lsr p11, p12, p11               // p11 = mask = 0xffff >> p11
        and p12, p1, p11                // x12 = _cmd & mask
    #else
    #error Unsupported cache mask storage for ARM64.
    #endif
    
              // 得到方法缓存的位置
        add p12, p10, p12, LSL #(1+PTRSHIFT)
                         // p12 = buckets + ((_cmd & mask) << (1+PTRSHIFT)) 
             // 读取 imp 和 sel
        ldp p17, p9, [x12]      // {imp, sel} = *bucket
    // 对比 取到的 imp 和参数的imp
    1:  cmp p9, p1          // if (bucket->sel != _cmd)
    // 如果不相等,跳转到 CheckMiss
        b.ne    2f          //     scan more
    // 如果相等,调用或者返回
        CacheHit $0         // call or return imp
        
    2:  // not hit: p12 = not-hit bucket
    // 没有直接找到方法缓存,且当前bucket放了其他方法,需要处理哈希碰撞
        CheckMiss $0            // miss if bucket->sel == 0
    // 比较 p12 和 p10, 
        cmp p12, p10        // wrap if bucket == buckets
    // 相等表示判断到了哈希表的开头
        b.eq    3f 
    // 读取数据到p13 然后  x12 = x12 - BUCKET_SIZE; 
        ldp p17, p9, [x12, #-BUCKET_SIZE]!  // {imp, sel} = *--bucket
        b   1b          // loop
    // 当前读取到了 buckets 的开头 ,再从最后一个开始算
    3:  // wrap: p12 = first bucket, w11 = mask
    #if CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_HIGH_16
        add p12, p12, p11, LSR #(48 - (1+PTRSHIFT))
                        // p12 = buckets + (mask << 1+PTRSHIFT)
    #elif CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_LOW_4
        add p12, p12, p11, LSL #(1+PTRSHIFT)
                        // p12 = buckets + (mask << 1+PTRSHIFT)
    #else
    #error Unsupported cache mask storage for ARM64.
    #endif
    
        // Clone scanning loop to miss instead of hang when cache is corrupt.
        // The slow path may detect any corruption and halt later.
    
        ldp p17, p9, [x12]      // {imp, sel} = *bucket
    1:  cmp p9, p1          // if (bucket->sel != _cmd)
        b.ne    2f          //     scan more
        CacheHit $0         // call or return imp
        
    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
    
    LLookupEnd$1:
    LLookupRecover$1:
    3:  // double wrap
        JumpMiss $0
    
    .endmacro
    

    找到缓存的处理方式

    // CacheHit: x17 = cached IMP, x12 = address of cached IMP, x1 = SEL, x16 = isa
    .macro CacheHit
    .if $0 == NORMAL
        TailCallCachedImp x17, x12, x1, x16 // authenticate and call imp
    .elseif $0 == GETIMP
        mov p0, p17
        cbz p0, 9f          // don't ptrauth a nil imp
        AuthAndResignAsIMP x0, x12, x1, x16 // authenticate imp and re-sign as IMP
    9:  ret             // return IMP
    .elseif $0 == LOOKUP
        // No nil check for ptrauth: the caller would crash anyway when they
        // jump to a nil IMP. We don't care if that jump also fails ptrauth.
        AuthAndResignAsIMP x17, x12, x1, x16    // authenticate imp and re-sign as IMP
        ret             // return imp via x17
    .else
    .abort oops
    .endif
    .endmacro
    
    .macro TailCallCachedImp
        // $0 = cached imp, $1 = address of cached imp, $2 = SEL, $3 = isa
        eor $1, $1, $2  // mix SEL into ptrauth modifier
        eor $1, $1, $3  // mix isa into ptrauth modifier
        brab    $0, $1
    .endmacro
    

    找不到缓存的处理方式

    .macro CheckMiss
        // miss if bucket->sel == 0
    .if $0 == GETIMP
        cbz p9, LGetImpMiss
    .elseif $0 == NORMAL
        cbz p9, __objc_msgSend_uncached
    .elseif $0 == LOOKUP
        cbz p9, __objc_msgLookup_uncached
    .else
    .abort oops
    .endif
    .endmacro
    
    .macro JumpMiss
    .if $0 == GETIMP
        b   LGetImpMiss
    .elseif $0 == NORMAL
        b   __objc_msgSend_uncached
    .elseif $0 == LOOKUP
        b   __objc_msgLookup_uncached
    .else
    .abort oops
    .endif
    .endmacro
    

    __objc_msgSend_uncached

    
        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
        TailCallFunctionPointer x17
    
        END_ENTRY __objc_msgSend_uncached
    

    MethodTableLookup

    .macro MethodTableLookup
        
        // push frame
        SignLR
        stp fp, lr, [sp, #-16]!
        mov fp, sp
    
        // save parameter registers: x0..x8, q0..q7
        sub sp, sp, #(10*8 + 8*16)
        stp q0, q1, [sp, #(0*16)]
        stp q2, q3, [sp, #(2*16)]
        stp q4, q5, [sp, #(4*16)]
        stp q6, q7, [sp, #(6*16)]
        stp x0, x1, [sp, #(8*16+0*8)]
        stp x2, x3, [sp, #(8*16+2*8)]
        stp x4, x5, [sp, #(8*16+4*8)]
        stp x6, x7, [sp, #(8*16+6*8)]
        str x8,     [sp, #(8*16+8*8)]
    
        // lookUpImpOrForward(obj, sel, cls, LOOKUP_INITIALIZE | LOOKUP_RESOLVER)
        // receiver and selector already in x0 and x1
        mov x2, x16
        mov x3, #3
        bl  _lookUpImpOrForward
    
        // IMP in x0
        mov x17, x0
        
        // restore registers and return
        ldp q0, q1, [sp, #(0*16)]
        ldp q2, q3, [sp, #(2*16)]
        ldp q4, q5, [sp, #(4*16)]
        ldp q6, q7, [sp, #(6*16)]
        ldp x0, x1, [sp, #(8*16+0*8)]
        ldp x2, x3, [sp, #(8*16+2*8)]
        ldp x4, x5, [sp, #(8*16+4*8)]
        ldp x6, x7, [sp, #(8*16+6*8)]
        ldr x8,     [sp, #(8*16+8*8)]
    
        mov sp, fp
        ldp fp, lr, [sp], #16
        AuthenticateLR
    
    .endmacro
    

    查找方法

    如果方法缓存里找不到方法,则会脱离汇编,使用高级语言进行下一流程

    lookUpImpOrForward

    NEVER_INLINE
    IMP lookUpImpOrForward(id inst, SEL sel, Class cls, int behavior)
    {
    // 进行消息转发的imp
        const IMP forward_imp = (IMP)_objc_msgForward_impcache;
        IMP imp = nil;
        Class curClass;
    
        runtimeLock.assertUnlocked();
    // 如果类还没有初始化,就不缓存
        if (slowpath(!cls->isInitialized())) {
            behavior |= LOOKUP_NOCACHE;
        }
    // 加锁
        runtimeLock.lock();
    
    /*
    防止创建 看起来像类但实际上不是类的二进制Blob并进行CFI攻击。 
    要确保这是一个内置于二进制文件中或通过objc_duplicateClass,objc_initializeClassPair或objc_allocateClassPair合法注册的类。
         // TODO:此检查在流程启动期间非常昂贵。
    */
        checkIsKnownClass(cls);
        //判断是否初始化,如果没有,需要先初始化
        cls = realizeAndInitializeIfNeeded_locked(inst, cls, behavior & LOOKUP_INITIALIZE);
        runtimeLock.assertLocked();
        curClass = cls;
    
    /*
    在加锁后, 以前会再次查找方法缓存, 但是在大多数情况下,证据表明大部分时间都未命中,因此会浪费时间。
    于是去掉了代码
    //唯一没有执行某种缓存查找的代码路径就是class_getInstanceMethod()。
    */
        //unreasonableClassCount--表示类的迭代的上限
        //(猜测这里递归的原因是attempts在第一次循环时作了减一操作,然后再次循环时,仍在上限的范围内,所以可以继续递归)
        for (unsigned attempts = unreasonableClassCount();;) {
            if (curClass->cache.isConstantOptimizedCache(/* strict */true)) {
    #if CONFIG_USE_PREOPT_CACHES
                imp = cache_getImp(curClass, sel);
                if (imp) goto done_unlock;
                curClass = curClass->cache.preoptFallbackClass();
    #endif
            } else {
            // 在方法列表查找方法
                Method meth = getMethodNoSuper_nolock(curClass, sel);
                if (meth) {
                    imp = meth->imp(false);
                    goto done;
                }
            // 如果没有父类
                if (slowpath((curClass = curClass->getSuperclass()) == nil)) {
    //--未找到方法实现,方法解析器也不行,使用转发
                    imp = forward_imp;
                    break;
                }
            }
    
            // Halt if there is a cycle in the superclass chain.
            //如果父类链中存在循环,则停止
            if (slowpath(--attempts == 0)) {
                _objc_fatal("Memory corruption in class list.");
            }
    
            // Superclass cache. 查找父类缓存
            imp = cache_getImp(curClass, sel);
            if (slowpath(imp == forward_imp)) {
                // 如果在父类中找到了forward,则停止查找,且不缓存,首先调用此类的方法解析
                break;
            }
            if (fastpath(imp)) {
              //如果在父类中,找到了此方法,将其存储到cache中
                goto done;
            }
        }
    
        // No implementation found. Try method resolver once.
       //没有找到方法实现,尝试一次方法解析
        if (slowpath(behavior & LOOKUP_RESOLVER)) {
            //动态方法决议的控制条件,表示流程只走一次
            behavior ^= LOOKUP_RESOLVER;
            return resolveMethod_locked(inst, sel, cls, behavior);
        }
    
     done:
        if (fastpath((behavior & LOOKUP_NOCACHE) == 0)) {
    #if CONFIG_USE_PREOPT_CACHES
            while (cls->cache.isConstantOptimizedCache(/* strict */true)) {
                cls = cls->cache.preoptFallbackClass();
            }
    #endif
     //存储到缓存
            log_and_fill_cache(cls, imp, sel, inst, curClass);
        }
     done_unlock:
        runtimeLock.unlock();
    //如果不需要找了(LOOKUP_NIL),并且imp等于forward_imp,就返回nil。
        if (slowpath((behavior & LOOKUP_NIL) && imp == forward_imp)) {
            return nil;
        }
        return imp;
    }
    
    forward_imp的赋值

    forward_imp是通过_objc_msgForward_impcache函数赋值的。全局搜索一下。发现这个函数是个汇编函数,找到其实现:

            STATIC_ENTRY __objc_msgForward_impcache
    
        // No stret specialization.
        b   __objc_msgForward
    
        END_ENTRY __objc_msgForward_impcache
    
        
        ENTRY __objc_msgForward
    
        adrp    x17, __objc_forward_handler@PAGE
        ldr p17, [x17, __objc_forward_handler@PAGEOFF]
        TailCallFunctionPointer x17
        
        END_ENTRY __objc_msgForward
    
    objc_defaultForwardHandler(id self, SEL sel)
    {
        _objc_fatal("%c[%s %s]: unrecognized selector sent to instance %p "
                    "(no message forward handler is installed)", 
                    class_isMetaClass(object_getClass(self)) ? '+' : '-', 
                    object_getClassName(self), sel_getName(sel), self);
    }
    

    这就是我们在日常开发中最常见的错误:没有实现函数,运行程序,崩溃时报的错误提示:unrecognized selector sent to instance
    forward_imp的值就是未找到imp的函数实现

    在方法列表里找方法
    static method_t *
    getMethodNoSuper_nolock(Class cls, SEL sel)
    {
        runtimeLock.assertLocked();
    
        ASSERT(cls->isRealized());
        // fixme nil cls? 
        // fixme nil sel?
    // 获得方法列表,是一个二维数组,每一个元素存储了分类或者类的方法
        auto const methods = cls->data()->methods();
    // 遍历方法
        for (auto mlists = methods.beginLists(),
                  end = methods.endLists();
             mlists != end;
             ++mlists)
        {
            method_t *m = search_method_list_inline(*mlists, sel);
            if (m) return m;
        }
        return nil;
    }
    
    

    search_method_list_inline

    ALWAYS_INLINE static method_t *search_method_list_inline(const method_list_t *mlist, SEL sel)
    {
        //方法列表是否排序过
        int methodListIsFixedUp = mlist->isFixedUp();
        int methodListHasExpectedSize = mlist->entsize() == sizeof(method_t);
    
        if (fastpath(methodListIsFixedUp && methodListHasExpectedSize)) {
    // 二分查找
            return findMethodInSortedMethodList(sel, mlist);
        } else {
            // 遍历查找
            for (auto& meth : *mlist) {
                if (meth.name == sel) return &meth;
            }
        }
    
    #if DEBUG 
    // 如果找不到,测试是否出了问题,并抛出异常
        // sanity-check negative results
        if (mlist->isFixedUp()) {
            for (auto& meth : *mlist) {
                if (meth.name == sel) {
                    _objc_fatal("linear search worked when binary search did not");
                }
            }
        }
    #endif
    
        return nil;
    }
    

    二分法查找方法 findMethodInSortedMethodList

    ALWAYS_INLINE static method_t * findMethodInSortedMethodList(SEL key, const method_list_t *list)
    {
        ASSERT(list);
    
        const method_t * const first = &list->first;
        const method_t *base = first;
        const method_t *probe;
        uintptr_t keyValue = (uintptr_t)key;
        uint32_t count;
        
        for (count = list->count; count != 0; count >>= 1) {
            probe = base + (count >> 1);
            
            uintptr_t probeValue = (uintptr_t)probe->name;
            
            if (keyValue == probeValue) {
                // `probe` is a match.
                // Rewind looking for the *first* occurrence of this value.
                // This is required for correct category overrides.
                while (probe > first && keyValue == (uintptr_t)probe[-1].name) {
                    probe--;
                }
                return (method_t *)probe;
            }
            
            if (keyValue > probeValue) {
                base = probe + 1;
                count--;
            }
        }
        
        return nil;
    }
    

    填充方法缓存

    log_and_fill_cache

    static void log_and_fill_cache(Class cls, IMP imp, SEL sel, id receiver, Class implementer)
    {
    #if SUPPORT_MESSAGE_LOGGING
        if (slowpath(objcMsgLogEnabled && implementer)) {
            bool cacheIt = logMessageSend(implementer->isMetaClass(), 
                                          cls->nameForLogging(),
                                          implementer->nameForLogging(), 
                                          sel);
            if (!cacheIt) return;
        }
    #endif
        cache_fill(cls, sel, imp, receiver);
    }
    

    cache_fill

    void cache_fill(Class cls, SEL sel, IMP imp, id receiver)
    {
        runtimeLock.assertLocked();
    
    #if !DEBUG_TASK_THREADS
        // Never cache before +initialize is done
        if (cls->isInitialized()) {
            cache_t *cache = getCache(cls);
    #if CONFIG_USE_CACHE_LOCK
            mutex_locker_t lock(cacheUpdateLock);
    #endif
    // 插入方法缓存
            cache->insert(cls, sel, imp, receiver);
        }
    #else
        _collecting_in_critical();
    #endif
    }
    

    insert

    ALWAYS_INLINE void cache_t::insert(Class cls, SEL sel, IMP imp, id receiver)
    {
    #if CONFIG_USE_CACHE_LOCK
        cacheUpdateLock.assertLocked();
    #else
        runtimeLock.assertLocked();
    #endif
    
        ASSERT(sel != 0 && cls->isInitialized());
    
        // Use the cache as-is if it is less than 3/4 full
        mask_t newOccupied = occupied() + 1;
        unsigned oldCapacity = capacity(), capacity = oldCapacity;
    // 如果是空表,需要去创建表
        if (slowpath(isConstantEmptyCache())) {
            // Cache is read-only. Replace it.
            if (!capacity) capacity = INIT_CACHE_SIZE;
            reallocate(oldCapacity, capacity, /* freeOld */false);
        }
        else if (fastpath(newOccupied + CACHE_END_MARKER <= capacity / 4 * 3)) {// 小于3/4
            // Cache is less than 3/4 full. Use it as-is.
        }
        else {// 扩容
            capacity = capacity ? capacity * 2 : INIT_CACHE_SIZE;
            if (capacity > MAX_CACHE_SIZE) {
                capacity = MAX_CACHE_SIZE;
            }
            reallocate(oldCapacity, capacity, true);
        }
    
        bucket_t *b = buckets();
        mask_t m = capacity - 1;
        mask_t begin = cache_hash(sel, m);
        mask_t i = begin;
    
    /*
    扫描第一个未使用的插槽,然后将其插入。 
    保证有一个空插槽,因为我们将认为调整为3/4就满了。
    */
        do {
            if (fastpath(b[i].sel() == 0)) {
                incrementOccupied();
    // 插入数据
                b[i].set<Atomic, Encoded>(sel, imp, cls);
                return;
            }
            if (b[i].sel() == sel) { // 如果已经缓存
                // The entry was added to the cache by some other thread
                // before we grabbed the cacheUpdateLock.
                return;
            }
        } while (fastpath((i = cache_next(i, m)) != begin));
        // 插入失败,抛出异常
        cache_t::bad_cache(receiver, (SEL)sel, cls);
    }
    

    动态方法解析

    resolveMethod_locked

    static NEVER_INLINE IMP resolveMethod_locked(id inst, SEL sel, Class cls, int behavior)
    {
        runtimeLock.assertLocked();
        ASSERT(cls->isRealized());
    
        runtimeLock.unlock();
    
        if (! cls->isMetaClass()) { 
        // 如果不是元类直接调用resolveInstanceMethod方法
            resolveInstanceMethod(inst, sel, cls);
        } 
        else {
        //调用元类的  resolveClassMethod: 和  resolveInstanceMethod: 方法
            resolveClassMethod(inst, sel, cls);
            if (!lookUpImpOrNil(inst, sel, cls)) {
                // 调用找不到实例方法
                resolveInstanceMethod(inst, sel, cls);
            }
        }
        return lookUpImpOrForward(inst, sel, cls, behavior | LOOKUP_CACHE);
    }
    

    resolveInstanceMethod

    static void resolveInstanceMethod(id inst, SEL sel, Class cls)
    {
        runtimeLock.assertUnlocked();
        ASSERT(cls->isRealized());
        SEL resolve_sel = @selector(resolveInstanceMethod:);
    
        if (!lookUpImpOrNil(cls, resolve_sel, cls->ISA())) {
            // Resolver not implemented.
            return;
        }
    
        BOOL (*msg)(Class, SEL, SEL) = (typeof(msg))objc_msgSend;
         // 调用 resolveInstanceMethod 方法
        bool resolved = msg(cls, resolve_sel, sel);
    
      // 重新查找方法
        IMP imp = lookUpImpOrNil(inst, sel, cls);
    // 打印一些信息
        if (resolved  &&  PrintResolving) {
            if (imp) {
                _objc_inform("RESOLVE: method %c[%s %s] "
                             "dynamically resolved to %p", 
                             cls->isMetaClass() ? '+' : '-', 
                             cls->nameForLogging(), sel_getName(sel), imp);
            }
            else {
                // Method resolver didn't add anything?
                _objc_inform("RESOLVE: +[%s resolveInstanceMethod:%s] returned YES"
                             ", but no new implementation of %c[%s %s] was found",
                             cls->nameForLogging(), sel_getName(sel), 
                             cls->isMetaClass() ? '+' : '-', 
                             cls->nameForLogging(), sel_getName(sel));
            }
        }
    }
    

    resolveClassMethod

    static void resolveClassMethod(id inst, SEL sel, Class cls)
    {
        runtimeLock.assertUnlocked();
        ASSERT(cls->isRealized());
        ASSERT(cls->isMetaClass());
    
        if (!lookUpImpOrNil(inst, @selector(resolveClassMethod:), cls)) {
            // Resolver not implemented.
            return;
        }
    
        Class nonmeta;
        {
            mutex_locker_t lock(runtimeLock);
            nonmeta = getMaybeUnrealizedNonMetaClass(cls, inst);
            // +initialize path should have realized nonmeta already
            if (!nonmeta->isRealized()) {
                _objc_fatal("nonmeta class %s (%p) unexpectedly not realized",
                            nonmeta->nameForLogging(), nonmeta);
            }
        }
        BOOL (*msg)(Class, SEL, SEL) = (typeof(msg))objc_msgSend;
        bool resolved = msg(nonmeta, @selector(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(inst, sel, cls);
    
        if (resolved  &&  PrintResolving) {
            if (imp) {
                _objc_inform("RESOLVE: method %c[%s %s] "
                             "dynamically resolved to %p", 
                             cls->isMetaClass() ? '+' : '-', 
                             cls->nameForLogging(), sel_getName(sel), imp);
            }
            else {
                // Method resolver didn't add anything?
                _objc_inform("RESOLVE: +[%s resolveClassMethod:%s] returned YES"
                             ", but no new implementation of %c[%s %s] was found",
                             cls->nameForLogging(), sel_getName(sel), 
                             cls->isMetaClass() ? '+' : '-', 
                             cls->nameForLogging(), sel_getName(sel));
            }
        }
    }
    

    lookUpImpOrNil

    static inline IMP
    lookUpImpOrNil(id obj, SEL sel, Class cls, int behavior = 0)
    {
        return lookUpImpOrForward(obj, sel, cls, behavior | LOOKUP_CACHE | LOOKUP_NIL);
    }
    

    消息转发

    _objc_msgForward_impcache

    STATIC_ENTRY __objc_msgForward_impcache
    
        // No stret specialization.
        b   __objc_msgForward
    
        END_ENTRY __objc_msgForward_impcache
    
        
        ENTRY __objc_msgForward
    
        adrp    x17, __objc_forward_handler@PAGE
        ldr p17, [x17, __objc_forward_handler@PAGEOFF]
        TailCallFunctionPointer x17
        
        END_ENTRY __objc_msgForward
    

    _objc_forward_handler

    #if !__OBJC2__
    
    // Default forward handler (nil) goes to forward:: dispatch.
    void *_objc_forward_handler = nil;
    void *_objc_forward_stret_handler = nil;
    
    #else
    
    // Default forward handler halts the process.
    __attribute__((noreturn, cold)) void
    objc_defaultForwardHandler(id self, SEL sel)
    {
        _objc_fatal("%c[%s %s]: unrecognized selector sent to instance %p "
                    "(no message forward handler is installed)", 
                    class_isMetaClass(object_getClass(self)) ? '+' : '-', 
                    object_getClassName(self), sel_getName(sel), self);
    }
    void *_objc_forward_handler = (void*)objc_defaultForwardHandler;
    

    代码跟读到此为止,通过模拟崩溃可以得知

        frame #10: 0x0000000108d4f610 CoreFoundation`+[NSObject(NSObject) doesNotRecognizeSelector:] + 132
        frame #11: 0x0000000108d45032 CoreFoundation`___forwarding___ + 1489
        frame #12: 0x0000000108d47068 CoreFoundation`__forwarding_prep_0___ + 120
      * frame #13: 0x0000000108247b36 测试`main(argc=1, argv=0x00007ffee79b7cb0) at main.m:10:9
    

    调用了一个不开源的 ___forwarding___ 方法,
    伪代码如下
    其实就是调用 forwardingTargetForSelector,methodSignatureForSelector,forwardInvacation 方法

    int __forwarding__(void *frameStackPointer, int isStret) {
        id receiver = *(id *)frameStackPointer;
        SEL sel = *(SEL *)(frameStackPointer + 8);
        const char *selName = sel_getName(sel);
        Class receiverClass = object_getClass(receiver);
    
        // 调用 forwardingTargetForSelector:
        if (class_respondsToSelector(receiverClass, @selector(forwardingTargetForSelector:))) {
            id forwardingTarget = [receiver forwardingTargetForSelector:sel];
            if (forwardingTarget && forwardingTarget != receiver) {
                return objc_msgSend(forwardingTarget, sel, ...);
            }
        }
    
        // 调用 methodSignatureForSelector 获取方法签名后再调用 forwardInvocation
        if (class_respondsToSelector(receiverClass, @selector(methodSignatureForSelector:))) {
            NSMethodSignature *methodSignature = [receiver methodSignatureForSelector:sel];
            if (methodSignature && class_respondsToSelector(receiverClass, @selector(forwardInvocation:))) {
                NSInvocation *invocation = [NSInvocation _invocationWithMethodSignature:methodSignature frame:frameStackPointer];
    
                [receiver forwardInvocation:invocation];
    
                void *returnValue = NULL;
                [invocation getReturnValue:&value];
                return returnValue;
            }
        }
    
        if (class_respondsToSelector(receiverClass,@selector(doesNotRecognizeSelector:))) {
            [receiver doesNotRecognizeSelector:sel];
        }
    
        // The point of no return.
        kill(getpid(), 9);
    }
    
    

    相关文章

      网友评论

          本文标题:objc_msgSend 源码跟踪

          本文链接:https://www.haomeiwen.com/subject/hzfjgktx.html