Runtimes 基础

1. Runtimes概念

OC 是一门动态语言,他不仅需要编译器,还需要运行时系统进行代码编译.Runtimes 就执行了这个运行时系统代码编译工作.
Runtime就是运行时,是Apple用C和汇编语言编写的一套C语言的API,它正是 Objective-C这门动态语言的核心。计算机唯一能识别的语言是机器语言,高级编程语言需要先编译为汇编语言,再由汇编语言编译为机器语言才能被计算机识别。而 Objective-C语言不能被直接编译为汇编语言,它必须先编译为C语言,然后再编译为汇编语言。而Runtime正是编译器将我们写的 Objective-C代码编译为C语言时用到的核心库。
C语言,静态语言,在编译阶段就要决定调用哪些函数属性,如果函数未实现就会编译报错。
OC语言,动态语言,在编译阶段并不能决定真正调用哪些函数,只要函数声明过即使没有实现也不会报错。

2. 消息机制的基本原理

OC 中 方法调用都是类似 [receiver selector]; 的形式,其本质就是让对象在运行时发送消息的过程。
[receiver selector]; 在编译阶段与运行阶段分别作了什么
编译阶段:[receiver selector]; 方法被编译器转换为:
objc_msgSend(receiver,selector) (不带参数)
objc_msgSend(recevier,selector,org1,org2,…)(带参数)
运行时阶段:消息接受者 recevier 寻找对应的 selector。
通过 recevier 的 isa 指针 找到 recevier 的 Class(类);
在 Class(类) 的 cache(方法缓存) 的散列表中寻找对应的 IMP(方法实现);
如果在 cache(方法缓存) 中没有找到对应的 IMP(方法实现) 的话,就继续在 Class(类) 的 method list(方法列表) 中找对应的 selector,如果找到,填充到 cache(方法缓存) 中,并返回 selector;
如果在 Class(类) 中没有找到这个 selector,就继续在它的 superClass(父类)中寻找;
一旦找到对应的 selector,直接执行 recevier 对应 selector 方法实现的 IMP(方法实现)。
若找不到对应的 selector,消息被转发或者临时向 recevier 添加这个 selector 对应的实现方法,否则就会发生崩溃。
在上述过程中涉及了好几个新的概念:objc_msgSend、isa 指针、Class(类)、IMP(方法实现) 等,下面我们来具体讲解一下各个概念的含义。

3. Runtime 用来做什么

3.1 基本作用

1.工程基本功能调用
2.在程序运行过程中,动态的创建类,动态添加、修改这个类的属性和方法;
3.遍历一个类中所有的成员变量、属性、以及所有方法
4.消息传递、转发

3.1 项目中的典型用法

1.给系统分类添加属性、方法 objc_setAssociatedObject()
2.方法交换 (Method Swizzling)
3.获取对象的属性、私有属性
4.字典转换模型 yymodel
5.KVC、KVO
6.归档(编码、解码)
7.NSClassFromString class<->字符串
8.消息转发

4. Runtime 相关概念

1.isa Class对象,指向objc_class结构体的指针,也就是这个Class的MetaClass(元类)

  • 类的实例对象的 isa 指向该类;该类的 isa 指向该类的 MetaClass
  • MetaCalss的isa对象指向RootMetaCalss
  • 如果MetaClass是RootMetaCalss,那么该MetaClass的isa指针指向它自己
    1.super_class Class对象指向父类对象
  • 如果该类的对象已经是RootClass,那么这个super_class指向nil
  • MetaCalss的SuperClass指向父类的MetaCalss
  • MetaCalss是RootMetaCalss,那么该MetaClass的SuperClass指向该对象的RootClass

经典示意图

image.png
  • ivars 类中所有属性的列表,使用场景:我们在字典转换成模型的时候需要用到这个列表找到属性的名称,去取字典中的值,KVC赋值,或者直接Runtime赋值

  • methodLists 类中所有的方法的列表,类中所有方法的列表,使用场景:如在程序中写好方法,通过外部获取到方法名称字符串,然后通过这个字符串得到方法,从而达到外部控制App已知方法。

  • cache 主要用于缓存常用方法列表,每个类中有很多方法,我平时不用的方法也会在里面,每次运行一个方法,都要去methodLists遍历得到方法,如果类的方法不多还行,但是基本的类中都会有很多方法,这样势必会影响程序的运行效率,所以cache在这里就会被用上,当我们使用这个类的方法时先判断cache是否为空,为空从methodLists找到调用,并保存到cache,不为空先从cache中找方法,如果找不到在去methodLists,这样提高了程序方法的运行效率

  • protocols 故名思义,这个类中都遵守了哪些协议,使用场景:判断类是否遵守了某个协议上

5.Runtime消息机制

  1. 消息发送
  • 找到方法 -->调用方法 -->方法回调
  • 首先消息发动给对象,遍历对象结构体的methodLists,找到对象响应的方法实现,如果没有就向上查找其父类的方法列表,一单找到就会调用方法,将接受对象的指针传给他
  • cache 在进行列表查询时总是很消耗时间的,所以添加了方法cache的概念 将刚刚使用的方法,常用的方法加入cache中,提高方法寻找速度.

6.Runtime消息转发

在使用过程中当5中的方法不能再方法列表中找到的时候,就会报错,抛出异常,
提示找不到方法实现

解决方案
1.实现方法,需要写代码主动实现代码

2.runtime转发,找不到方法来实现,那我们就换发方法来实现吧

  • 方法找不到实现方法时会调用该函数,
    +(BOOL)resolveInstanceMethod:(SEL)sel 实例方法解析
    +(BOOL)resolveClassMethod:(SEL)sel 类方法解析

  • 消息转发第一次方法解析中没有处理方法将会调用
    -(id)forwardingTargetForSelector:(SEL)aSelector
    将未知SEL作为参数传入,寻找另外对象处理,如果可以处理,返回该对象

  • 当我们在前面两个步骤都没有处理该未知SEL时,就会到第三个步骤
    -(void)doesNotRecognizeSelector:(SEL)aSelector

  • 如果第三步也没能实现
    (void)doesNotRecognizeSelector:(SEL)aSelector
    调用崩溃

  • 流程图


    image.png

7.Runtime消息机制使用

1.分类添加属性
核心代码 手动实现get 与set方法

  • (void)setName:(NSString *)name {
    objc_setAssociatedObject(self, &nameKey, name, OBJC_ASSOCIATION_COPY);
    }
  • (NSString *)name {
    return objc_getAssociatedObject(self, &nameKey);
    }
    2.Method Swizzling 方法交换
    这是runtime的灵魂所在 , B哥一点 这可以当做简单的代码HOOK
    甲方
    BOOL class_addMethod(Class cls, SEL name, IMP imp, const char *types)
    减方法
    class_replaceMethod(Class _Nullable cls, SEL _Nonnull name, IMP _Nonnull imp,
    const char * _Nullable types)
    方法交换
    method_exchangeImplementations(Method _Nonnull m1, Method _Nonnull m2)

原有被交换方法
Method originalMethod = class_getInstanceMethod(class, originalSelector);

要交换的分类新方法
Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector);

class_replaceMethod(class,swizzledSelector,method_getImplementation(originalMethod),method_getTypeEncoding(originalMethod));

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

推荐阅读更多精彩内容

  • 我们常常会听说 Objective-C 是一门动态语言,那么这个「动态」表现在哪呢?我想最主要的表现就是 Obje...
    Ethan_Struggle阅读 2,170评论 0 7
  • 本文转载自:http://yulingtianxia.com/blog/2014/11/05/objective-...
    ant_flex阅读 744评论 0 1
  • 本文详细整理了 Cocoa 的 Runtime 系统的知识,它使得 Objective-C 如虎添翼,具备了灵活的...
    lylaut阅读 791评论 0 4
  • 继上Runtime梳理(四) 通过前面的学习,我们了解到Objective-C的动态特性:Objective-C不...
    小名一峰阅读 739评论 0 3
  • 关于OC中的消息发送的实现,在去年也看过一次,当时有点不太理解,但是今年再看却很容易理解。 我想这跟知识体系的构建...
    咖啡绿茶1991阅读 932评论 0 1