这篇关于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. python3+redis问题求解

    学生管理系统   更新学生信息没做出来,找个大神补全下.谢谢. # 记录: # bug:操作后若退出需要两次退出才行. 待修复 # 下一步:链接redis进行使用. # 更新学生库信息 待完成 imp ...

  2. ServiceStack.Redis记录

    一.事务 (一)AcquireLock方法 1.说明:申请对一个Key加锁(期间其他对象不能访问). 2.带using的使用,或带过期时间参数,否则锁不会自动释放. using (RedisCache ...

  3. 【转载】关于在centos下安装python3.7.0以上版本时报错ModuleNotFoundError: No module named '_ctypes'的解决办法

    3.7版本需要一个新的包libffi-devel,安装此包之后再次进行编译安装即可. #yum install libffi-devel -y #make install 原文:https://blo ...

  4. WPF中TreeView控件的使用案例

    WPF总体来说还是比较方便的,其中变化最大的主要是Listview和Treeview控件,而且TreeView似乎在WPF是一个备受指责的控件,很多人说他不好用.我这个demo主要是在wpf中使用Tr ...

  5. JS 实现触发下载内容(H5 download)

    概述 我对使用js控制下载非常感兴趣,在网上查资料的时候碰巧看到了相关实现方法,记录下来供以后开发时参考,相信对其他人也有用. 参考资料: JS前端创建html或json文件并浏览器导出下载 理解DO ...

  6. 嘿!我用python帮我干这些事

    python 无疑是当下火上天的语言,但是我们又不拿来工作,那么能拿来干啥呢?我是这么干的. 1. 平时工作开发用不上,就当个计算器吧! python # 加减乘除 >>> (3 + ...

  7. 彻底填平Static坑(细节决定成败)

    static 学习的过程就是填坑的过程,可不要偷懒想着跳过去,你现在跳过去,就相当于给自己挖了一个坑,你迟早会掉进去的,为了避免不掉坑,所以,努力填坑吧! 一.如果没有static会怎样? 需求: 1 ...

  8. 一位90后的自述:如何从年薪3w到30w

    作者介绍:90后生人/男/二本本科/世界500强技术主管 1.引言 上海小胖,曾就职于pwc(普华永道)担任TechLeader,带领DS(Data Scientist)团队完成全美医疗保险大数据项目 ...

  9. yolov3中 预测的bbox如何从特征图映射到原图?

    Anchor Box的边框 选取标准的k-means(欧式距离来衡量差异),在box的尺寸比较大的时候其误差也更大,而我们希望的是误差和box的尺寸没有太大关系.所以通过IOU定义了如下的距离函数,使 ...

  10. vue 项目实战 (生命周期钩子)

    开篇先来一张图 下图是官方展示的生命周期图 Vue实例的生命周期钩子函数(8个)        1. beforeCreate             刚 new了一个组件,无法访问到数据和真实的do ...