美文网首页
2020-02-23 Runtime

2020-02-23 Runtime

作者: Coke26 | 来源:发表于2020-02-23 21:15 被阅读0次
    目录:
    01-Runtime 初探
    02-Runtime 对象与方法的本质
    03-Runtime 动态方法解析
    04-Runtime 消息转发
    05-Runtime应用

    01-Runtime 初探

    runtime:c /C++汇编一起写成的api ——》OC运行时
    运行时:装在到内存 提供运行时功能

    底层库关系

    // 代码 ---> 编译链接 ---> 执行
    // 对于C函数就是静态性,我编译如果不存在这个run函数,就会报错,但是OC不一样


    02-Runtime 对象与方法的本质

    //clang 成C++后:
    eg:

    LGPerson *p = [[LGPerson alloc] init];
    //   LGPerson *p = ((LGPerson *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("LGPerson"), sel_registerName("new"));
    
    

    总结:任何OC方法的调用都会编译成C++的一句代码:objc_msgSend

    OC 对象 -- 本质 --- 结构体
    方法的本质 --- 发送消息

    // objc_msgSend ?
    // 消息的组成

           [p run];
            // ((void (*)(id, SEL))(void *)objc_msgSend)((id)p, sel_registerName("run"));
    
    **
             `void *)objc_msgSend)((id)p `消息接受者
             `sel_registerName("run") ` 方法编号 --- name
            ` imp` 函数实现的指针 -- sel -> imp ?
            // 下层通讯  方法 -- 对象  类
            // 父类发送消息
            // github 写了注释的代码
    **
    ---
    sel -> imp ?
    如何通过sel找到imp指针呢
    
    ##2.1类方法与实例方法
    (**类的类方法** 和 **元类的对象实例方法** 是一样的东西)
    runtime三种调用方式
    1.runtime api
    2.NSObject    `isMenberof iskindof`
    3.OC上层方法  `@selector`
    
    ---
    objc_msgSend: 实现的两种方式
    1.快速: 缓存 汇编
    2.慢速: C 和 C++ 和 汇编 一起完成
    
    ![缓存在对象结构体中的位置](https://img.haomeiwen.com/i2144862/2fb22ab8fa663526.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
    
    objc_msgSend1快速流程:
    cache_t 找方法,objc_msgSend通过sel找对应的imp,如果在cache_t里面,通过一个hash表找到;
    objc_msgSend2慢速流程:
    找不到就会用C look up 往上找,复杂的过程,找到后存到cache_t
    
    ###为什么要用汇编写objc_msgSend?
    1.C无法实现: 写一个函数,保存位置的参数,跳转任意指针;
    2.汇编可以:使用 寄存器   x0  x31,运行速度快;
    
    
    ---
    (tagged pointer  特殊的数据类型)
    
    ##2.2源码分析流程,方法查找:(汇编部分,runtime源码)上往下执行;
    `_objc_msgSend`
    `LNilOrTagged`
    `LGetIsaDone` isa处理完毕
    

    LGetIsaDone:
    CacheLookup NORMAL // calls imp or objc_msgSend_uncached

    `CacheLookup NORMAL` // calls imp or objc_msgSend_uncached
    人话:处理完isa后 cache查找imp,如果能找就call imp(快速),找不到返回`objc_msgSend_uncached`
    
    进入
    `.macro CacheLookup`
    

    .macro CacheLookup
    // x1 = SEL, x16 = isa
    ldp x10, x11, [x16, #CACHE] // x10 = buckets, x11 = occupied|mask
    and w12, w1, w11 // x12 = _cmd & mask
    add x12, x10, x12, LSL #4 // x12 = buckets + ((_cmd & mask)<<4)

    ldp x9, x17, [x12]      // {x9, x17} = *bucket
    

    1: cmp x9, x1 // if (bucket->sel != _cmd)
    b.ne 2f // scan more
    CacheHit $0 // call or return imp

    2: // not hit: x12 = not-hit bucket
    CheckMiss $0 // miss if bucket->sel == 0
    cmp x12, x10 // wrap if bucket == buckets
    b.eq 3f
    ldp x9, x17, [x12, #-16]! // {x9, x17} = *--bucket
    b 1b // loop

    3: // wrap: x12 = first bucket, w11 = mask
    add x12, x12, w11, UXTW #4 // x12 = buckets+(mask<<4)

    // Clone scanning loop to miss instead of hang when cache is corrupt.
    // The slow path may detect any corruption and halt later.
    
    ldp x9, x17, [x12]      // {x9, x17} = *bucket
    

    1: cmp x9, x1 // if (bucket->sel != _cmd)
    b.ne 2f // scan more
    CacheHit $0 // call or return imp

    2: // not hit: x12 = not-hit bucket
    CheckMiss $0 // miss if bucket->sel == 0
    cmp x12, x10 // wrap if bucket == buckets
    b.eq 3f
    ldp x9, x17, [x12, #-16]! // {x9, x17} = *--bucket
    b 1b // loop

    3: // double wrap
    JumpMiss $0

    .endmacro

    三种情况
    `CacheHit`直接返回
    `CheckMiss`找不到,进入`objc_msgSend_uncached`
    `add`没有,add当前的imp到缓存cache
    ---
    ###2.2.1慢速查找流程分析:上往下执行;
    ####`objc_msgSend_uncached`开始:
    **进入`_class_lookupMethodAndLoadCache3`,此处走出汇编进入`objc-runtime-new.mm`(c/c++混编部分)**
    

    IMP _class_lookupMethodAndLoadCache3(id obj, SEL sel, Class cls)
    {
    return lookUpImpOrForward(cls, sel, obj,
    YES/initialize/, NO/cache/, YES/resolver/);
    }

    `lookUpImpOrForward`
    `realizeClass(cls)`
    `_class_initialize`
    `imp = cache_getImp(cls, sel)` 为了 `remap(`重映射 ?
    `漫长过程`  找方法 ——>找方法——>找自己——>找老爹——>NSObject
    
    
    

    //当前找:
    // Try this class's method lists.
    {
    Method meth = getMethodNoSuper_nolock(cls, sel);
    if (meth) {
    log_and_fill_cache(cls, meth->imp, sel, inst, cls);
    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;
            }
        }
    }
    
    
    ---
    #03-Runtime 动态方法解析
    `_class_resolveMethod`
    

    //找自己找父类找不到,就执行一次动态方法解析
    // No implementation found. Try method resolver once.

    if (resolver  &&  !triedResolver) {
        runtimeLock.unlockRead();
        _class_resolveMethod(cls, sel, inst);
        runtimeLock.read();
        // 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;
    }
    

    pragma mark - 动态方法解析 重写

    • (BOOL)resolveInstanceMethod:(SEL)sel{
      NSLog(@"来了 老弟");
      return [super resolveInstanceMethod:sel];
      }

    • (BOOL)resolveClassMethod:(SEL)sel{
      NSLog(@"来了 老弟");
      return [super resolveClassMethod:sel];
      }

    `lookup imp没有`
    `_class_resolveMethod` 动态方法决议,只调用一次;
    
    调用前判断:if不是元类 
    是,调用`resolveInstanceMethod`
    否则调用`resolveClassMethod`
    
    #04-Runtime 消息转发
    //类消息转发
    //只有汇编调用 没有源码实现
    
    ![动态方法决议
    ](https://img.haomeiwen.com/i2144862/937a2617f32cf856.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
    
    
    //系统调用堆栈
    //沙盒 
    //切面变成
    //家庭作业 aspect ——— 消息转发代码
    
    //method-swizzling hook array 数组越界
    //self.dataArray objectAtIndex  
    //if  index < self.count-1 -- exception
    //消息转发
    ---
    
    #5Runtime应用
    
    
    
    
    

    相关文章

      网友评论

          本文标题:2020-02-23 Runtime

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