美文网首页
runtime-消息机制

runtime-消息机制

作者: Berning | 来源:发表于2021-02-05 16:52 被阅读0次

    OC方法调用,其实都是转换为objc_msgSend函数调用
    objc_msgSend的执行流程可以分为3大阶段:

    • 消息发送
    • 动态方法解析
    • 消息转发

    流程图

    objc_msgSend.png

    objc_msgSend执行流程 - 源码

    消息发送

    • objc-msg-arm64.s
    -> ENTRY _objc_msgSend
    -> LNilOrTagged
      b.eq  LReturnZero     // nil check
      b.ne  LGetIsaDone
    -> LGetIsaDone:
        CacheLookup NORMAL, _objc_msgSend
    -> .macro CacheLookup NORMAL
    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    
        cmp p12, p10        // wrap if bucket == buckets
        b.eq    3f
        ldp p17, p9, [x12, #-BUCKET_SIZE]!  // {imp, sel} = *--bucket
        b   1b          // loop
    -> .macro CheckMiss
      cbz   p9, __objc_msgSend_uncached
    -> STATIC_ENTRY __objc_msgSend_uncached
        MethodTableLookup
    -> .macro MethodTableLookup
      bl    _lookUpImpOrForward    //   jmp2  lookUpImpOrForward objc_runtime-new.mm 
    
    • objc_runtime-new.mm

    消息发送(方法查找)

    -> lookUpImpOrForward
    -> getMethodNoSuper_nolock
    -> search_method_list_inline
    方列列表有序则:二分查找 findMethodInSortedMethodList
    方法列表无序则:遍历

    若找到方法:
    -> log_and_fill_cache -> cache_fill -> getCache { return &cls->cache; } -> insert
    若没有找到方法:
    -> 判断curClass = curClass->superclass 是否 nil

    • 若curClass不为nil
      ->cache_getImp -> log_and_fill_cache -> getMethodNoSuper_nolock -> log_and_fill_cache ...
    • 若curClass为nil,则进入动态方法解析

    动态方法解析

    imp = forward_imp; break 跳出循环,进行动态方法解析
    -> resolveMethod_locked -> resolveInstanceMethod (resolveClassMethod)
    ->lookUpImpOrForward -> cache_getImp

    • cache_getImp 有值
      ->done_nolock
    • cache_getImp 无值,重复消息发送阶段,直到curClass为nil,
      imp = forward_imp = (IMP)_objc_msgForward_impcache; 进入消息转发阶段
    //消息发送 /动态方法解析/消息转发
    IMP lookUpImpOrForward(id inst, SEL sel, Class cls, int behavior) {
        const IMP forward_imp = (IMP)_objc_msgForward_impcache;
        Class curClass;
        IMP imp = nil;
    
        // 动态方法解析调用,动态添加方法时执行
        if (fastpath(behavior & LOOKUP_CACHE)) {
            imp = cache_getImp(cls, sel);
            if (imp) goto done_nolock;
        }
    
        for (unsigned attempts = unreasonableClassCount();;) {
            Method meth = getMethodNoSuper_nolock(curClass, sel); //查找方法
          //若找到方法,jmp2 done
            if (meth) {
                imp = meth->imp;
                goto done;
            }
    
            if (slowpath((curClass = curClass->superclass) == nil)) { //父类赋值当前类,并判断是否为nil,若为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.");
            }
    
            // 查找父类缓存
            imp = cache_getImp(curClass, sel);
            if (slowpath(imp == forward_imp)) {
                break;
            }
          //若父类缓存存在,jmp2 done
            if (fastpath(imp)) {
                // Found the method in a superclass. Cache it in this class.
                goto done;
            }
        }//end for
    
    //动态方法解析
        if (slowpath(behavior & LOOKUP_RESOLVER)) {
            behavior ^= LOOKUP_RESOLVER;
            return resolveMethod_locked(inst, sel, cls, behavior);
        }
    
     done:
        log_and_fill_cache(cls, imp, sel, inst, curClass);
        runtimeLock.unlock();
     done_nolock:
        if (slowpath((behavior & LOOKUP_NIL) && imp == forward_imp)) {
            return nil;
        }
        return imp;
    }
    
    //方法查找
    static method_t *getMethodNoSuper_nolock(Class cls, SEL sel){
        
         //cls -> data() 获得class_rw_t 表
        //cls->data()->methods()  获取method_array_t 二维数组{method_list_t,method_list_t }
        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;
        }
    }
    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;
    }
    
     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;
    }
    
    
    
    
    
    //动态方法解析
    static NEVER_INLINE IMP
    resolveMethod_locked(id inst, SEL sel, Class cls, int behavior)
    {
        runtimeLock.assertLocked();
        ASSERT(cls->isRealized());
    
        runtimeLock.unlock();
    
        if (! cls->isMetaClass()) {
            // try [cls resolveInstanceMethod:sel]
            resolveInstanceMethod(inst, sel, cls);
        } 
        else {
            // try [nonMetaClass resolveClassMethod:sel]
            // and [cls resolveInstanceMethod:sel]
            resolveClassMethod(inst, sel, cls);
            if (!lookUpImpOrNil(inst, sel, cls)) {
                resolveInstanceMethod(inst, sel, cls);
            }
        }
    
        // chances are that calling the resolver have populated the cache
        // so attempt using it
        return lookUpImpOrForward(inst, sel, cls, behavior | LOOKUP_CACHE);
    }
    
    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;
        bool resolved = msg(cls, resolve_sel, sel);
    
        // Cache the result (good or bad) so the resolver doesn't fire next time.
        // +resolveInstanceMethod adds to self 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 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));
            }
        }
    }
    
    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));
            }
        }
    }
    
    
    

    消息转发

    • objc-msg-arm64.s
    -> STATIC_ENTRY __objc_msgForward_impcache
    ->  ENTRY __objc_msgForward
    -> Core Foundation
    -> __forwarding__(不开源)
    
    

    相关文章

      网友评论

          本文标题:runtime-消息机制

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