2020-02-23 Runtime

2020-02-23  本文已影响0人  Coke26
目录:
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 - 动态方法解析 重写

`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应用




上一篇下一篇

猜你喜欢

热点阅读