美文网首页
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://minilv.github.io/2019/03/17/Runtime-消息机制土味讲解/?nsu...

  • Runtime-消息机制

    Objective-C是一门动态语言,类型的判断、类的成员变量、方法的内存地址都是在程序的运行阶段才最终确定,并且...

  • runtime-消息机制

    从异常说起 我们都知道,在iOS中存在这么一个通用类类型id,它可以用来表示任何对象的类型 —— 这意味着我们使用...

  • runtime-消息机制

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

  • Runtime-原理

    runtime初探对象与方法的本质runtime-消息发送runtime-动态方法解析runtime-消息转发 r...

  • Runtime(消息转发机制)

    1、什么是Runtime Runtime->运行时机制,最主要的是消息机制,是一套比较底层的纯C语言API,属于一...

  • Runtime-(三)消息传递机制

    我们知道在OC中,所有的方法调用最终都会转换成objc_msgSend形式的方法调用。如下图: 而对于调用父类的方...

  • 底层12:Runtime-消息机制

    面试题:讲一下OC的消息机制。 OC中的方法调用其实都是转成了objc_msgSend函数的调用,给receive...

  • Runtime-消息传递的流程机制

    缓存查找流程 根据给定的方法选择器SEL,来查找bucket_t中具体的方法实现IMP bucket_t是方法缓存...

  • runtime-消息传递与转发机制

    参考文章:继承自NSObject的不常用又很有用的函数【重点推荐】Objective-C Runtime 1小时入...

网友评论

      本文标题:runtime-消息机制

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