Objecive-C语言中RunTime机制

为了充分介绍Objecive-C语言中RunTime机制,使读者对RunTime机制有一个清晰的了解。本文将从以下几个部分来介绍相关知识:

  • 第一部分将介绍Objecive-C语言的基本特性。
  • 第二部分将介绍Objecive-C类对象与isa指针的本质。
  • 第三部分将介绍RunTime整个流程。
  • 第四部分将介绍RunTime的常见应用场合。

第一部 Objecive-C语言基本特性

Objecive-C(以下简称OC)是一种采用消息结构、面向运行时、动态的面向对象的语言。其运行时所执行的代码由运行环境来决定。它本质上是C的超集。
OC中重要的工作由“运行期组建”(runtime component)而非编译器完成。

备注:一般来说面向对象的语言有两种实现形式:一种是消息结构,另一种是采用函数调用。它们两者的区别在于。后者运行时所执行的代码由编译器决定,也就是说采用函数调用的语言在编译期编译器就会查找出运行是代码所执行的方法。采用消息结构(Objecive-C)语言只有在运行时确定所执行的代码方法。

第二部 Objecive-C中对象与isa指针。

“对象”是面向对象语言的基本构造单元,开发者可以通过对象来存储和传递数据。理解“对象”对于理解消息传递机制有着重要的意义。

要认识什么是isa指针,我们得先明确一点:
在OC中,任何类的定义都是对象。类和类的实例(对象)没有任何本质上的区别。任何对象都有isa指针。

描述OC对象所用的数据类型定义在运行期程序文件的头文件里:

struct objc_object {
    Class isa  OBJC_ISA_AVAILABILITY;};
    typedef struct objc_object *id;```
由此可见每个对象的首个成员时Class类的变量,该对象称为isa指针。它指向对象的类。

类的定义也在头文件里:

@interface NSObject <NSObject> {
Class isa OBJC_ISA_AVAILABILITY;
}```
看出来NSObject定义了一个成员变量,我们继续寻找:

typedef struct objc_class *Class;
/// Represents an instance of a class.

Class 是一个 objc_class 结构类型的指针, id是一个 objc_object 结构类型的指针。

继续打开objc_class,我们找到class的定义:

struct objc_class {
    Class isa  OBJC_ISA_AVAILABILITY;
#if !__OBJC2__
    Class super_class                                        &OBJC2_UNAVAILABLE;
    const char *name                                         OBJC2_UNAVAILABLE;
    long version                                             OBJC2_UNAVAILABLE;
    long info                                                OBJC2_UNAVAILABLE;
    long instance_size                                       OBJC2_UNAVAILABLE;
    struct objc_ivar_list *ivars                             OBJC2_UNAVAILABLE;
    struct objc_method_list **methodLists                    OBJC2_UNAVAILABLE;
    struct objc_cache *cache                                 OBJC2_UNAVAILABLE;
    struct objc_protocol_list *protocols                     OBJC2_UNAVAILABLE;
#endif
} OBJC2_UNAVAILABLE;```

从上可以看出:isa本质上是一个Class 类型的指针。每个实例对象有个isa的指针,他指向对象的类。而Class里也有个isa的指针, 指向meteClass(元类)。元类保存了类方法的列表。当类方法被调用时,先会从元类查找类方法的实现,如果没有,元类会向他父类查找该方法。同时注意的是:元类(meteClass)也是类,它也是对象。元类也有isa指针,它的isa指针最终指向的是一个根元类(root meteClass).根元类的isa指针指向本身,这样形成了一个封闭的内循环。

下面这张图很好的解释了类的继承关系:
![449095-3e972ec16703c54d.png](http://upload-images.jianshu.io/upload_images/449095-b5ed6ceedfbc2b39.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

每一个对象本质上都是一个类的实例。其中类定义了成员变量和成员方法的列表。对象通过对象的isa指针指向类。

每一个类本质上都是一个对象,类其实是元类(meteClass)的实例。元类定义了类方法的列表。类通过类的isa指针指向元类。

所有的元类最终继承一个根元类,根元类isa指针指向本身,形成一个封闭的内循环。

####第三部分:RunTime机制
RunTime指一个程序在运行(或者在被执行)的环境。简称运行时。它指的是系统在运行的时候的一些机制,其中最主要的是消息机制。

OC中RunTime本质上是一套比较底层的纯C语言API, 属于1个C语言库, 包含了很多底层的C语言API。 在我们平时编写的OC代码中, 程序运行过程时, 其实最终都是转成了runtime的C语言代码。RunTime提供了一些使得对象之间能够传递消息的重要函数,并且包含了创建类实例所用的全部逻辑。

#####基础知识:
在OC中,給某个对象传递消息可以这样写:
 `    id returnValue = [obj messageName:parameter];`
其中obj是接受对象,messageName是选择子(selector),parameter是参数。
我们把selector和parameter结合起来称为消息。

编译器会把以上消息转化为C语言函数调用:
`id returnValue = objc_msgSend(id obj,SEL messageName,parameter).`
`objc_msgSend`是消息传递机制中核心函数。其原型如下:
` void  objc_msgSend(id self,SEL cmd,...).`

消息传递机制主要分为以下几个阶段:
#####第一阶段:消息传递机制(pass a message)
编译把OC中方法转化objc_msgSend形式后。objc_msgSend函数首先通过接受对象(obj)的isa指针找到接收对象(obj)对应的类(class)。在类(Class)中先去cache中 通过选择子(SEL)查找对应函数的方法method(),若 cache中未找到。再去方法列表(methodList)中查找,若方法列表(methodList)中未找到,则去superClass中查找。若能找到,则将method加 入到cache中,以方便下次查找,并通过method中的函数指针跳转到对应的函数中去执行。若没有找到,消息传递机制将进入第二阶段消息转发阶段(消息转发机制)。

缓存:每一个类都都有一块缓存(cache)。其存储的是“快速映射表”。objc_msgSend会将方法的匹配结果缓存在快速映射表中。

消息派发系统会将类的方法列表的名称映射到相关方法的实现上。以此来找到应该调用的方法。这些方法均以函数指针(IMP)的形式来表示。其原型是:
`id(*IMP)(id,SEL,...)`

方法调配技术:运行此特性可以改变相关方法的实现。
NSString 有可以响应以下几个方法:
![417684A6-0D92-4EE3-961C-B16FFE6D5ED8.png](http://upload-images.jianshu.io/upload_images/449095-9843bfca776f234e.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
OC运行系统提供了几个方法能够操控这张表。开发者可以向其中新增选择子,也可以改变选择子的实现,还可以交换选择子所映射的指针。经过几次操作可以变成以下:
![0D1876D1-7098-42E2-B4B3-08AF769CE77F.png](http://upload-images.jianshu.io/upload_images/449095-840df668989dc4ad.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
对比两张表,我们可以发现下面新增加了一个选择子。交换两个选择子的实现。可以看出方法调配技术的强大之处。

交换两个方法的实现,参数为方法的实现。
`void method_exchangeImplementations(Method m1,Method m2)`

得到方法的实现:
 `  Method class_getInstanceMethod(Class aClsaa,SEL aSelector)`

实际应用中交换两个方法的实现意义不大。一般是在类别中新增加一个方法,然后交换两个方法的实现。以此来改变类的方法的实现。
![消息传递](http://upload-images.jianshu.io/upload_images/449095-fca5ec800efe1bb9.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
#####第二阶段:消息转发机制(message forwarding)
消息转发机制分为两个阶段:
######1.动态方法解析:
第一阶段运行系统会先征询接受者所属的类看其是否动态添加方法以处理当前的这个未知选择子(SEL)。
在对象收到无法解读的消息后,便会调用所属类的下列方法:
`+(BOOL)resolveInstanceMethod:(SEL)selector`
该方法参数就是需要处理的未知选择子。返回值是表示这个类是否能新增一个方法处理这个未知选择子。在处罚完整消息转发机制前本类有机会增加一个方法处理这个未知选择子。在此类方法中可以通过`class_addMethod` 增加一个方法处理未知选择子。
如果是类方法将会触发下面这个方法:
`+(BOOL)resolveClassMethod:(SEL)selector`

######2.完整的消息转发机制:
如果第一阶段没有成功的处理未知选择子,那么接受者自己再也没有办法动态的添加方法来响应该未知选择子的消息了 。此时运行系统会启动完整的消息转发机制。这里又分为两个小阶段:

**第一阶段**:会请接受者看看有没有其他对象能处理这条消息。若有,运行系统会把消息转给那个对象(备援的接受者),消息转发结束 。若没有将进入第二阶段。
`-(id)forwardingTargetForSelector:(SEL)selector`
在此方法中若当前接受者能找到备援对象,那将其返回。若找不到返回nil,触发下一个阶段。

**第二阶段**:运行系统会把消息的全部细节封装到NSInvocation对象中,再給接受者一次机会,另其解决当前未知选择子(SEL)。
   
如果消息转发到了这一个阶段,运行系统就会启用完整的消息转发机制。首先创建NSInvocation对象。把消息的全部细节封装于其中。此对象包括选择子,目标对象和参数。触发NSInvocation对象时,消息派发系统(message-dispathch-system)将亲自把消息指派給目标对象。

此步骤运行系统会调用下面方法来处理:
` -(void)forwardingInvocation:(NSInvocation)invocation。`
这个方法可以有多种实现形式。可以很简单,改变消息的调用目标,使消息在新的目标上调用。这个备援接受者是相同的原理。可以为改变消息的内容,参数。使其可以被目标对象接受。也可以更换选择子等。

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

推荐阅读更多精彩内容