基本简介

1、根据官方文档,OC有一个特性:它会尽可能把一些决定从编译时和链接时推迟到运行时才处理,所以这门语言需要的就不只是一个编译器,它还需要一个runtime系统来处理那些已经被编译过的代码。

2、runtime有两种:legacy runtime和modern runtime,区别在于:

(1)、在legacy runtime中,如果你修改了一个类的实例变量之后,你需要重新编译一下;

(2)、在modern runtime中,在这种情况下你无需再重新编译一次。

3、OC的程序和runtime系统有三个层次的交互方式:

(1)、大部分时候,当你直接使用OC代码的时候,runtime系统便在运作了。这其中一个最主要的功能便是消息发送(Messaging);

(2)、使用一些NSObject类的方法,比如description或isKindOfClass: 、isMemberOfClass:等方法(其实大部分的类都是继承自NSObject,都可以使用这些方法,也有一些继承自其它类,比如继承自NSProxy类);

(3)、直接调用runtime提供的方法。

消息发送(Messaging)的概念

4、在讨论消息发送之前,我们先要明白一个概念:方法的selector是什么?

方法的selector是根据这个方法的方法名计算得出的一个key,通过这个key可以得到反推出这个方法的方法名。由于selector是根据方法名得出的,所以同名的方法的selector都是相同的。

而在一个类中,为了让一个selector可以唯一标识出一个方法,于是在同一个类里就不允许有两个同名的方法,即使参数不同也不行。(所以OC没有方法重载)

方法除了selector之外,另外还有一个组成部分是方法实现(procedure ,也即是method implementation),指的是方法的实现代码。

所以可以这么理解:方法就是它的selector和实现的统称。

同时需要注意的是,一个方法的selector和实现之间并不是固定对应的,消息发送的过程其实就是通过方法的selector找到它的实现的过程。

5、消息发送的基本流程:

在OC里面,当我们“调用”方法的时候,使用的是:

[receiver message]

这样的语法,这句代码其实并不会立刻就去执行message方法的代码,在这句代码和message方法的代码之间,有一个消息发送(Messaging)的过程。

编译器把这一句代码编译成了:

objc_msgSend(receiver, selector)

如果方法有多个参数的话,会连同参数被编译成:

objc_msgSend(receiver, selector, arg1, arg2, ...)

然后这个objc_msgSend()函数执行的过程便是消息发送的过程了。

消息其实就是objc_msgSend()函数的所有参数的并称,可以理解为:消息(Message)就是方法调用者、方法的selector和方法的参数等细节的集合。

消息发送的过程其实就是objc_msgSend()函数会根据receiver和selector参数找到对应的方法实现代码(procedure ,也即是method implementation)并执行它的过程。

在这个过程中,objc_msgSend()函数是如何找到对应的类,找到对应的方法实现的呢?并且如果在查找的过程中发生了意外的话,它又会怎么处理呢?

在研究这些问题之前,我们先要了解一下runtime相关的一些数据结构。

runtime相关的数据结构

6、我们从objc_msgSend()这个方法的定义入手,来研究一下这些数据结构,objc_msgSend()的定义如下:

id objc_msgSend(id self, SEL op, ...)

它有两个主要的参数,一个是id类型的,一个SEL类型的。

(1)、首先来看SEL这个类型,我们可以找到它的定义如下:

没有找到struct objc_selector进一步的定义,但是我们通过字面可以知道这个结构体便是方法,或者是能够唯一标识出这个方法的一些信息,而SEL作为一个指向selector的指针,便可理解为指向某个方法的指针了;

(2)、objc_msgSend()的另一个主要参数是id类型的,我们可以找到id类型的定义如下:

所以id其实是一个指向struct objc_object结构体的指针,而struct objc_object这个结构体包含了一个成员isa,这个成员的数据类型是Class,那么接下来就来看看Class是什么;

(3)、Class的定义如下:

我们可以知道,Class是一个指向struct objc_class结构体的指针,再进一步找到struct objc_class结构体的定义,如下:

可以看到struct objc_class结构体包含了很多成员,我们一个一个来查看这些成员。

7、struct objc_class:

(1)、首先我们可以神奇地发现,struct objc_class结构体的第一个成员的数据类型竟然也是Class,由上面我们已经知道Class是一个指向struct objc_class结构体的指针,所以struct objc_class结构体的第一个成员也是一个同样叫做isa 的struct objc_class结构体。

这说明了,类本身也是某种类的实例。这个先按下不表,在后文(9)中再做讨论;

(2)、struct objc_class结构体(以下直接称为“类”)的第二个成员仍然是一个Class类型的数据super_class,表示这个结构体对象对应的父类;

(3)、version和info都是存储着类相关的信息,当某个类的实例变量发生变化的时候,它的version的内容会跟着发生变化;

(4)、instance_size是这个类生成的实例对象的大小;

(5)、成员ivars指针指向了struct objc_ivar_list结构体类型的数据,我们可以找到struct objc_ivar_list结构体这种数据的定义:

可以看到其中最主要的成员是一个由struct objc_ivar结构体组成的数组ivar_list[],它便是这个类所包含的所有实例变量组成数组。

我们还可以进一步查看到struct objc_ivar结构体的定义:

所以其实一个struct objc_ivar结构体便是一个成员变量;

(6)、成员methodLists和ivars类似,我们先看一下struct objc_method_list这种数据类型的定义:

结构和struct objc_ivar_list基本相似,主要内容也是一个数组,由struct objc_method类型的数据组成,我们再看一下struct objc_method的定义:

可以发现struct objc_method类型的数据便是其实便是方法了。这个结构体最主要的成员是一个IMP类型的数据method_imp,它其实是一个指向方法实现代码的指针。

同时,可以注意到成员methodLists是一个二级指针,这决定了它可以更改指向的*methodLists,即是可以修改方法列表,这也是Category可以添加方法的原因;

(7)、成员cache用来存储经常调用的method,当对某个对象调用方法时,runtime会根据对象的isa指针找到对象对应的类,然后首先在类的cache中查到要调用的方法,当cache中找不到之后,才会去methodLists中查找;

(8)、成员protocols标识这个类遵循的协议;

(9)、前面说到,类本身也是某种类的实例,我们从Class第一个成员isa也是一个Class类型的的数据的出这个结论的。

类所属的类,称为元类(Meta Class),元类存储着一个类的所有类方法,类是这个元类的实例。当我们调用类方法的时候,其实把这个类当做一个实例向它发送消息:当我们向一个对象发送消息时,runtime会在这个对象所属的这个类的方法列表中查找方法;而向一个类发送消息时,会在这个类的元类的方法列表中查找。

再继续深究下去,发现元类也是一个类,那么它的isa指针又该指向哪里呢?这样一直延伸下去会无穷无尽,所以所有元类的isa指针会被指向同一个类——基类(Root Class)的元类,基类的元类的isa指针又指向了它自己,形成了一个闭环。

需要清楚一个概念:元类(meta class)和父类(super class)是两个不同的概念,一般一个类都会有个一个元类和一个父类。

元类是这个类作为一个对象看待时,它所属的类;父类是这个类作为类看待时,它继承而来的类。

它们的关系如下图:

可以注意到,基类(Root Class)其实就是NSObject,它的superclass指向nil。NSObject的元类的父类指针指向NSObject,最终在顶部形成了一个回路。

参考文档:

官方文档

https://github.com/samlaudev/RuntimeDemo

www.jianshu.com/p/25a319aee33d

格而知之6:我所理解的Runtime(1)的更多相关文章

  1. 格而知之3:Core Data的基本使用

    最近准备做一个随手笔记类的app给自己用,考虑到从未使用过Core Data,就决定用Core Data来做数据存储.在网上参考了一些Core Data的资料后,用一天的时间写了这个demo,主要测试 ...

  2. 理解 Objective-C Runtime

    初学 Objective-C(以下简称ObjC) 的人很容易忽略一个 ObjC 特性 —— ObjC Runtime.这是因为这门语言很容易上手,几个小时就能学会怎么使用,所以程序员们往往会把时间都花 ...

  3. 理解Objective-C Runtime(四)Method Swizzling

    Objective-C对象收到消息之后,究竟会调用何种方法需要在运行期间才能解析出来.那你也许会问:与给定的选择子名称相应的方法是不是也可以在runtime改变呢?没错,就是这样.若能善用此特性,则可 ...

  4. 格而知之16:我所理解的Block(2)

    11.那么Block到底是怎么实现的呢?试一试通过将Block 的代码转换成普通C语言代码来查看它的实现过程. 要将OC代码转换成C语言代码,可以使用clang编译的一个命令: 通过这个命令能把指定文 ...

  5. 格而知之16:我所理解的Block(3)

    23.在前文中的例子中,Block结构体里的isa指针还没有详细讲解,这个指针都被置向了_NSConcreteStackBlock,它标识了Block的类型. 其实除了_NSConcreteStack ...

  6. 格而知之15:我所理解的Block(1)

    1.Block 本质上是一个struct结构体,在这个结构体中,最重要的成员是一个函数(当然除函数外还有其他重要的成员). 2.在开始解析Block之前,首先来回顾一下Block的格式.Block相关 ...

  7. 格而知之8:我所理解的Runtime(3)

    关联对象 14.使用Category对类进行拓展的时候,只能添加方法,而不适合添加属性(可以添加属性,也可以正常使用get方法和set方法,只是不会自动生成以下划线开头命名的成员变量). 可以通过关联 ...

  8. 格而知之7:我所理解的Runtime(2)

    消息发送(Messaging) 8.以上便是runtime相关的一些数据结构,接下来我们回看一开始的疑问: objc_msgSend()函数在执行的过程中是如何找到对应的类,找到对应的方法实现的呢? ...

  9. 格而知之5:我所理解的Run Loop

    1.什么是Run Loop? (1).Run Loop是线程的一项基础配备,它的主要作用是来让某一条线程在有任务的时候工作.没有任务的时候休眠. (2).线程和 Run Loop 之间的关系是一一对应 ...

随机推荐

  1. handsontable的核心方法

    1.为handsontable添加钩子方法 addHook(key,callback):key为钩子方法名 <span style="font-size:18px;"> ...

  2. EasyUI 两个日期比较

    两个日期进行比较,后一个日期不能早于晚一个日期.需要自己去扩展validatebox的方法 1.对比2个密码是否相同 $.extend($.fn.validatebox.defaults.rules, ...

  3. [Regex Expression] Match mutli-line number, number range

    /^-?\d{,}\.\d+$/gm

  4. 常用LINUX脚本汇总(1)

    1.查看磁盘使用空间 df -hl 2.查看文件或者文件夹大小 du -sh 文件(夹)名  查看文件大小  AIX系统为du -sg 3.查看当前用户下定时任务列表crontab -l 4.修改定时 ...

  5. Hacker(十)----常用入侵工具

    黑客若想攻击目标计算机,仅靠DOS命令是无法完成的,还需要一些功能强大的入侵工具,如端口扫描工具.网络嗅探工具.木马制作工具及远程控制工具等. 一.端口扫描工具 端口扫描工具有扫描端口的功能,所谓端口 ...

  6. Oracle11g x64使用Oracle SQL Developer报错:Unable to find a Java Virtual Machine

    原因oracle 11g中安装的Oracle SQL Developer是32位的,而我们现在给他指定的java.exe却是64位的,所以会出现这种错误.解决方法1)从网上下载Oracle SQL D ...

  7. windows下adb(android调试桥)基本命令(持续更新。。。)

    前言:刚开始学习android(坚持每天1篇笔记哈^_^),比较实用的命令是adb,所以就先学习这些,主要用真机调试,模拟器用的是genymotion,所以emulator暂时不大需要哈,可以后续再补 ...

  8. oracle 经典语句集合

    1.一列转多行 方法一: select a.id,  substr(','||a.name||',',instr(','||a.name,',',1,b.rn)+1,    instr(a.name| ...

  9. UIAlertView与UIActionSheet

    1.UIAlertView(警告框) 1.1 创建警告框,设置样式 - (IBAction)alertView:(UIButton *)sender {//创建button按钮 //创建警告框的实例 ...

  10. 亲试,Windows平台上使用Qt5.2.1编写Android

    首先把工具都下载好: 1. Qt for Android: http://qt-project.org/downloads 2. Android NDK http://developer.androi ...