这篇关于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. Shell文本操作-5

  2. Senparc.Weixin.TenPay 正式发布

    微信支付刚出来的时候,和公众号的绑定关系很深(甚至旧版本使用的就是公众号的appId),随着微信生态的逐步丰富,微信支付越来越成为一个独立的平台,同时服务于公众号.小程序.开放平台.企业号/企业微信等 ...

  3. Nerd的畅销产品

    感谢陈大先生花费时间一起细致地讨论这个课题,促成此文. <Nerd的套现ATM机>中已经谈到,对于nerd来讲最畅销的产品是“考试通过”.本文将继续这个话题的展开讨论,更深入细致地分析这个 ...

  4. Javascript高级编程学习笔记(63)—— 事件(7)鼠标及滚轮事件

    鼠标与滚轮事件 鼠标事件是web开发中最常用的一类事件,毕竟鼠标是最主要的定位设备 DOM3级事件中定义了9个鼠标事件: click:在用户单击主鼠标按钮(一般为鼠标左键)或者按下回车时触发,这一点对 ...

  5. Javascript对象Oject的强制类型转换

    众所周知Javascript作为一种动态类型,弱类型的脚本语言其数据类型在很多时候都会发生类型转换.而这些类型转换往往都是隐式的,这让我们在使用Js的时候会产生许多麻烦.而Js的基础数据类型的转换在此 ...

  6. 微信小程序 - 相对定位和绝对定位 - 相对路径和绝对路径

    微信小程序 - 相对定位和绝对定位 相对定位relative,绝对定位absolute 相对定位:元素是相对自身进行定位,参照物是自己. 绝对定位:元素是相对离它最近的一个父级元素进行定位. 相对定位 ...

  7. python 字符串拼接

    str1 = 'abc' str2 = 'def' str3 = str1 + str2 print(str3) 这种方法只需要申请一次内存.

  8. 深入Vue2.x的虚拟DOM diff原理

    一.前言 Vue的核心是双向绑定和虚拟DOM(下文我们简称为vdom),关于双向绑定可以参阅木琴的文章<剖析Vue原理&实现双向绑定MVVM>,vdom是树状结构,其节点为vnod ...

  9. while true 死循环判断端口按顺序启动应用

    需求:spring微服务应用启动较慢并且要求一个应用启完才能启第二个应用. 思路:加了个while true 死循环判断端口启动了才启下一个应用. 执行方式:/appupgrade/spring_cl ...

  10. error) DENIED Redis is running in protected mode because protected mode is enabled报错

    官网地址:https://redis.io/download 官方安装文档如下: Installation Download, extract and compile Redis with: $ wg ...