这篇关于Runtime讲解参考https://juejin.im/post/593f77085c497d006ba389f0以及https://www.jianshu.com/p/6ebda3cd8052

针对iOS开发人员,相信每个人都对Runtime有所了解。面试官更是对Runtime非常钟爱,可能5分钟不到就决定offer属不属于你或者要更高的薪资,而面试官一般问了解Runtime嘛,使用过他嘛,应用场景有哪些以及底层如何实现的。文章读下来大约10-20分钟,建议先收藏起来。大神可绕过!

Runtime介绍

Objective-C是基于C的,它为C添加了面向对象的特性,它将很多静态语言在编译和链接时期做的事放到了runtime运行时来处理。Runtime(简称运行时)是一套纯C(C和汇编编写的)API,而OC就是运行时机制,也就是在运行时的一些机制,其中最主要的就是消息机制。对于C语言,函数的调用在编译的时候会决定调用哪一个函数。OC的函数调用成为消息发送,属于动态调用过程,在编译的时候并不能确定调用哪一个函数,只有在真正运行的时候,才会根据函数的名称找到对应的函数来调用。事实证明:在编译阶段,OC可以调用任何函数,即使这个函数并未实现,只要声明过就不会报错,只有在运行的时候,才会报错,这就是因为是运行时动态调用的,而C语言调用未使用的函数就会报错。

Runtime消息传递

消息机制原理:对象根据方法编号SEL去映射表查找对应的方法实现。

一个对象的方法[obj foo],编译器转为消息发送objc_msgSend(obj,foo),Runtime时执行的流程如下:

(1)通过obj的isa指针找到它的class

(2)在class的method list找foo

(3)如果class中没找到foo,继续往他的superclass中找

(4)一旦找到foo这个函数,就去执行它的实现IMP

换句而言:

(1)OC在向一个对象发送消息时,Runtime库会根据对象的isa指针找到该对象的类或其父类中查找方法。

(2)注册方法编号(可以快速查找)

(3)根据方法编号去查找对应方法

(4)找到只是最终函数实现地址,根据地址去方法区调用对应函数。

补充:每一个对象内部都有一个isa指针,这个指针指向它的真实类型,根据这个指针就能知道将来调用哪一个类的方法。

但这其中有一个问题,效率低。并不可能都需要遍历一次objc_method_list。如果把经常被调用的函数缓存下来,那可以大大提高效率。这就是objc_class 中另一个重要成员objc_cache做的事情,-再找到foo之后,把foo的method_name作为key,method_imp作为value给存起来,当再次收到foo消息的时候,可以直接在cache里找到,避免去遍历objc_method_list。

Runtime用到的概念

类对象(objc_class)

Objective-C类是由Class类型来表示的,它实际上指的是一个指向objc_class结构体的指针。

typedef struct object_class *Class

查看objc_class结构体的定义如下:

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

struct objc_class 结构体定义了很多变量,通过命名不难发现:

结构体保存了指向父类的指针,类的名字,版本,实例大小,方法列表,缓存以及遵守的协议列表等。类对象就是一个结构体,这个结构体存放的数据称为元数据。

实例(objc_object)

/// Represents an instance of a class.
struct objc_object {
Class isa OBJC_ISA_AVAILABILITY;
}; /// A pointer to an instance of a class.
typedef struct objc_object *id;

类对象中的元数据存储的都是如何创建一个实例的相关信息,那么类对象和类方法应该从哪里创建呢?

就是从isa指针指向的结构体创建,类对象isa指针指向的我们称为元类(metaclass)

元类(meta class)

通过上图我们可以看出整个体系构成了一个自闭环,struct_objc_object结构体实例他的isa指针指向的类对象。

类对象的isa指针指向了元类,super_class 指针指向了父类的类对象。

而元类的super_class 指针指向了父类的元类,那元类的isa指针又指向了自己。而基类的meta-classisa指针是指向它自己。

Method(objc_method)

先看下定义:就是表示能够独立完成一个功能的一段代码

runtime.h
/// An opaque type that represents a method in a class definition.代表类定义中一个方法的不透明类型
typedef struct objc_method *Method;
struct objc_method {
SEL method_name OBJC2_UNAVAILABLE;
char *method_types OBJC2_UNAVAILABLE;
IMP method_imp OBJC2_UNAVAILABLE;

SEL method_name: 方法名

Char *method_types:方法类型

IMP method_imp:方法实现

说明SEL和IMP其实都是Method的属性。

SEL(objc_selector)

Objc.h
/// An opaque type that represents a method selector.代表一个方法的不透明类型
typedef struct objc_selector *SEL

objc_msgSend函数第二个参数类型为SEL,它是selector在Objective-C中的表示类型,selector是方法选择器,可以理解为区分方法的ID,这个ID的数据结构为SEL:

@property SEL selector;

可以看到selectorSEL的一个实例。

其实selector就是个映射到方法的c字符串,你可以用Objective-C编译器命令@selector或者Runtime系统的sel_registerName函数获得一个SEL类型的方法选择器。

命名规则:

同一个类:selector不能重复

不同的类,selector可以重复

这也带来一个弊端,我们在写C代码的时候,经常会用到函数重载,就是函数名相同,参数不同,但是这在Objective-C中是行不通的,因为selector只记了methodname,没有参数,所以没法区分不同的method

- (void)caculate(NSInteger)num;
- (void)caculate(CGFloat)num;

这样会被报错的。

我们可以这样:

- (void)caculateWithInt(NSInteger)num;
- (void)caculateWithFloat(CGFloat)num;

在不同类中相同名字的方法所对应的方法选择器是相同的,即使方法名字相同而变量类型不同也会导致它们具有相同的方法选择器。

IMP

看下IMP的定义:就是指向最终实现程序的内存地址的指针。

在iOS的Runtime Method通过selector和IMP两个属性,实现了快速查询方法和实现,相对提高了性能,又保持了灵活性。

类缓存(objc_cache)

为了加速消息分发, 系统会对方法和对应的地址进行缓存,就放在上述的objc_cache,所以在实际运行中,大部分常用的方法都是会被缓存起来的,Runtime系统实际上非常快,接近直接执行内存地址的程序速度。

category(objc_category)

Category是表示一个指向分类的结构体的指针,其定义如下:

struct category_t {
const char *name;
classref_t cls;
struct method_list_t *instanceMethods;
struct method_list_t *classMethods;
struct protocol_list_t *protocols;
struct property_list_t *instanceProperties;
};
name:是指 class_name 而不是 category_name。
cls:要扩展的类对象,编译期间是不会定义的,而是在Runtime阶段通过name对 应到对应的类对象。
instanceMethods:category中所有给类添加的实例方法的列表。
classMethods:category中所有添加的类方法的列表。
protocols:category实现的所有协议的列表。
instanceProperties:表示Category里所有的properties,这就是我们可以通过objc_setAssociatedObject和objc_getAssociatedObject增加实例变量的原因,不过这个和一般的实例变量是不一样的。

分类中可以添加实例方法,类方法,甚至可以实现协议,添加属性,不可以添加成员变量。

下面将讲解Runtime(下)的应用

Runtime详解(上)的更多相关文章

  1. iOS开发-Runtime详解

    iOS开发-Runtime详解 简介 Runtime 又叫运行时,是一套底层的 C 语言 API,其为 iOS 内部的核心之一,我们平时编写的 OC 代码,底层都是基于它来实现的.比如: [recei ...

  2. 李洪强iOS经典面试题156 - Runtime详解(面试必备)

    李洪强iOS经典面试题156 - Runtime详解(面试必备)   一.runtime简介 RunTime简称运行时.OC就是运行时机制,也就是在运行时候的一些机制,其中最主要的是消息机制. 对于C ...

  3. IE8“开发人员工具”使用详解上(各级菜单详解)

    来源: http://www.cnblogs.com/JustinYoung/archive/2009/03/24/kaifarenyuangongju.html IE8“开发人员工具”使用详解上(各 ...

  4. C++框架_之Qt的窗口部件系统的详解-上

    C++框架_之Qt的窗口部件系统的详解-上 第一部分概述 第一次建立helloworld程序时,曾看到Qt Creator提供的默认基类只有QMainWindow.QWidget和QDialog三种. ...

  5. [js高手之路]深入浅出webpack教程系列2-配置文件webpack.config.js详解(上)

    [js高手之路]深入浅出webpack教程系列索引目录: [js高手之路]深入浅出webpack教程系列1-安装与基本打包用法和命令参数 [js高手之路]深入浅出webpack教程系列2-配置文件we ...

  6. SSL/TLS协议详解(上):密码套件,哈希,加密,密钥交换算法

    本文转载自SSL/TLS协议详解(上):密码套件,哈希,加密,密钥交换算法 导语 作为一名安全爱好者,我一向很喜欢SSL(目前是TLS)的运作原理.理解这个复杂协议的基本原理花了我好几天的时间,但只要 ...

  7. Linux常用命令详解上

    Linux常用命令详解上 目录 一.shell 二.Linux命令 2.1.内部命令与外部命令的区别 2.2.Linux命令行的格式 2.3.编辑Linux命令行的辅助操作 2.4.获得命令帮助的方法 ...

  8. Linux主要shell命令详解(上)

    [摘自网络] kill -9 -1即实现用kill命令退出系统 Linux主要shell命令详解 [上篇] shell是用户和Linux操作系统之间的接口.Linux中有多种shell,其中缺省使用的 ...

  9. Mybatis全面详解——上(学习总结)

    原文地址:https://blog.csdn.net/ITITII/article/details/79969447 一.什么是Mybatis 这里借用官网的一句话介绍什么是mybatis:MyBat ...

  10. 【史上最全】Hadoop 核心 - HDFS 分布式文件系统详解(上万字建议收藏)

    1. HDFS概述 Hadoop 分布式系统框架中,首要的基础功能就是文件系统,在 Hadoop 中使用 FileSystem 这个抽象类来表示我们的文件系统,这个抽象类下面有很多子实现类,究竟使用哪 ...

随机推荐

  1. 如何下载官网上下载历史Java版本(老版本Java)

    首先先打开Oracle的官网    -->Oracle 然后选择Trials and Downloads 然后往下翻,选择java(JDK) 然后看到了这个,再往下翻 点他,然后就是选择你想下载 ...

  2. STM32F10x_StdPeriph_Lib_V3.5.0标准库文件关系(转载他人)

  3. ICO图标下载地址

    http://findicons.com/ http://www.iconfont.cn/

  4. 【Solidity】学习(4)

    solidity函数修饰符 view  没有改变任何值或者写任何东西.只能读取,不能修改 function sayHello() public view returns (string) { } pu ...

  5. Bootstrap3.3.7

    页面的布局 <-!不让文字超出左右屏幕--> <style> .demo { word-wrap: break-word; } </style> 他们为那个模板中的 ...

  6. 【转载】sql monitor

    来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/27067062/viewspace-2129635/ SQL Monitor Report 1. SQL Monit ...

  7. 王垠的40行代码,究竟diao在哪里

    王垠是谁? 不用我说了吧!!! 别傻谈,亮码瞧! ;; A simple CPS transformer which does proper tail-call and does not;; dupl ...

  8. Windows环境下MySQL 5.6安装与配置

    1将MySQL压缩包解压到自定义目录下. 2.添加环境变量 右键单击我的电脑->属性->高级系统设置(高级)->环境变量. 点击系统变量下的新建按钮 1)    输入变量名:MYSQ ...

  9. 吴恩达机器学习笔记36-正则化和偏差/方差(Regularization and Bias_Variance)

    在我们在训练模型的过程中,一般会使用一些正则化方法来防止过拟合.但是我们可能会正则化的程度太高或太小了,即我们在选择λ 的值时也需要思考与刚才选择多项式模型次数类似的问题. 我们选择一系列的想要测试的

  10. Eclipse 中构建 Maven 项目的完整过程 - 动态 Web 项目

    进行以下步骤的前提是你已经安装好本地maven库和eclipse中的maven插件了(有的eclipse中已经集成了maven插件) 一.Maven项目的新建 1.鼠标右键---->New--- ...