基本简介

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. qt model/view 架构基础介绍之QListWidget

    # -*- coding: utf-8 -*- # python:2.x __author__ = 'Administrator' from PyQt4.QtGui import  * from Py ...

  2. web前端之 JS

    JavaScript概述 JavaScript是一门编程语言,简称js,由浏览器编译并运行,JS说白了就是让页面能够动起来 js存在形式 1.在html页面中 <script> alert ...

  3. H Language Blueprint

    H Language Blueprint I will design the H language in the very-soon future, it will be like: 1- a scr ...

  4. [RxJS] Reactive Programming - What is RxJS?

    First thing need to understand is, Reactive programming is dealing with the event stream. Event stre ...

  5. vue 单页面应用实战

    1. 为什么要 SPA? SPA: 就是俗称的单页应用(Single Page Web Application). 在移动端,特别是 hybrid 方式的H5应用中,性能问题一直是痛点. 使用 SPA ...

  6. linux修改系统时间

    当你把linux还原到某个点的时候,vmware帮不了你把系统时间也给重设了.所以这时候就要手工来搞.关于咋设linux时间.网上介绍也很多,但是都是抄来抄去的东西.那怎么才能高效快捷的设置系统时间呢 ...

  7. date命令小结

    date命令是查看日期时间的常用命令,date MMDDhhmmYY.ss(修改顺序)用来更改时间 linux时间分为系统时间和硬件时间, [root@www doc]# clock--------- ...

  8. 关于DCLP实现的单例模式的一些想法

    关于DCLP实现的单例模式的一些想法 我之前写过单例的文章( http://www.cnblogs.com/mkdym/p/4908644.html ),但是现在又有了一些想法,不想再在原来那篇文章上 ...

  9. Lucene学习总结之六:Lucene打分公式的数学推导

    在进行Lucene的搜索过程解析之前,有必要单独的一张把Lucene score公式的推导,各部分的意义阐述一下.因为Lucene的搜索过程,很重要的一个步骤就是逐步的计算各部分的分数. Lucene ...

  10. python-整理--pip whl命令

    如果要在windows系统上安装新的包,可以下载*.exe安装文件,双击下一步...,如果找不到exe的话. 在CMD中执行 pip install 安装包文件.whl 就可以安装了 pip这个命令本 ...