2020-02-23 Runtime

目录:
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://upload-images.jianshu.io/upload_images/2144862-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://upload-images.jianshu.io/upload_images/2144862-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应用




最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,607评论 6 507
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,239评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,960评论 0 355
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,750评论 1 294
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,764评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,604评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,347评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,253评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,702评论 1 315
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,893评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,015评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,734评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,352评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,934评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,052评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,216评论 3 371
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,969评论 2 355