Runtime详解(上)
这篇关于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-class的isa指针是指向它自己。
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;
可以看到selector是SEL的一个实例。
其实selector就是个映射到方法的c字符串,你可以用Objective-C编译器命令@selector或者Runtime系统的sel_registerName函数获得一个SEL类型的方法选择器。
命名规则:
同一个类:selector不能重复
不同的类,selector可以重复
这也带来一个弊端,我们在写C代码的时候,经常会用到函数重载,就是函数名相同,参数不同,但是这在Objective-C中是行不通的,因为selector只记了method的name,没有参数,所以没法区分不同的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详解(上)的更多相关文章
- iOS开发-Runtime详解
iOS开发-Runtime详解 简介 Runtime 又叫运行时,是一套底层的 C 语言 API,其为 iOS 内部的核心之一,我们平时编写的 OC 代码,底层都是基于它来实现的.比如: [recei ...
- 李洪强iOS经典面试题156 - Runtime详解(面试必备)
李洪强iOS经典面试题156 - Runtime详解(面试必备) 一.runtime简介 RunTime简称运行时.OC就是运行时机制,也就是在运行时候的一些机制,其中最主要的是消息机制. 对于C ...
- IE8“开发人员工具”使用详解上(各级菜单详解)
来源: http://www.cnblogs.com/JustinYoung/archive/2009/03/24/kaifarenyuangongju.html IE8“开发人员工具”使用详解上(各 ...
- C++框架_之Qt的窗口部件系统的详解-上
C++框架_之Qt的窗口部件系统的详解-上 第一部分概述 第一次建立helloworld程序时,曾看到Qt Creator提供的默认基类只有QMainWindow.QWidget和QDialog三种. ...
- [js高手之路]深入浅出webpack教程系列2-配置文件webpack.config.js详解(上)
[js高手之路]深入浅出webpack教程系列索引目录: [js高手之路]深入浅出webpack教程系列1-安装与基本打包用法和命令参数 [js高手之路]深入浅出webpack教程系列2-配置文件we ...
- SSL/TLS协议详解(上):密码套件,哈希,加密,密钥交换算法
本文转载自SSL/TLS协议详解(上):密码套件,哈希,加密,密钥交换算法 导语 作为一名安全爱好者,我一向很喜欢SSL(目前是TLS)的运作原理.理解这个复杂协议的基本原理花了我好几天的时间,但只要 ...
- Linux常用命令详解上
Linux常用命令详解上 目录 一.shell 二.Linux命令 2.1.内部命令与外部命令的区别 2.2.Linux命令行的格式 2.3.编辑Linux命令行的辅助操作 2.4.获得命令帮助的方法 ...
- Linux主要shell命令详解(上)
[摘自网络] kill -9 -1即实现用kill命令退出系统 Linux主要shell命令详解 [上篇] shell是用户和Linux操作系统之间的接口.Linux中有多种shell,其中缺省使用的 ...
- Mybatis全面详解——上(学习总结)
原文地址:https://blog.csdn.net/ITITII/article/details/79969447 一.什么是Mybatis 这里借用官网的一句话介绍什么是mybatis:MyBat ...
- 【史上最全】Hadoop 核心 - HDFS 分布式文件系统详解(上万字建议收藏)
1. HDFS概述 Hadoop 分布式系统框架中,首要的基础功能就是文件系统,在 Hadoop 中使用 FileSystem 这个抽象类来表示我们的文件系统,这个抽象类下面有很多子实现类,究竟使用哪 ...
随机推荐
- orm介绍
昨日回顾: 1 今日内容: 1 orm介绍 1 tools--->Run manage.py Task python3 manage.py makemigrations 只需要敲命令:makem ...
- XML生成XAMl扩展
所有的WPF控件列为枚举 代码如: 1 public enum ControlType 2 { 3 Window_Resources, 4 Page_Resources, 5 Grid, 6 Stac ...
- Python之配置日志的几种方式(logging模块)
原文:https://blog.csdn.net/WZ18810463869/article/details/81147167 作为开发者,我们可以通过以下3种方式来配置logging: 1)使用Py ...
- Djangoの1
有ip无路由是404,ip也无是无法访问此网站.url中?前的是路由,?后是GET请求的各组参数. 子项目和子应用下的两类urls.py:[]内的各个路由函数url,其首参网址的开头无/,结尾有/ ...
- hdu 4069 垃圾数独
首先dfs给每个格子分一个大的区块 其次套板子就a 我一开始直接在选取行的时候填数独,发现超时 我这一行也就4个元素,找到 x <= 81 的列计算元素位置,81 < x <= 16 ...
- prototype 和 __proto__
/* * 1.构造函数里有属性及方法----->其构造的实例对象直接通过new构造时获得这些属性和方法 * 2.构造函数里的原型prototype----->这个原型对象:包括1).原型对 ...
- Android中实现gif动画
一.需求 Android本身没有提供直接显示gif动画的相关控件,因此需要自定义GifImageView类来实现gif的播放,主要是使用的Movie类来解决的. 二.自定义GifImageView p ...
- datepart in ssis
"\\\\"+_"+ (DT_STR,4,1252)DATEPART( "yyyy" , @[System::StartTime] ) + RIGHT ...
- SpringBoot条件注解@Conditional
最近项目中使用到了关于@Conditional注解的一些特性,故写此文记录一下 @Conditional是啥呀? @Conditional注解是个什么东西呢,它可以根据代码中设置的条件装载不同的bea ...
- Spring boot 内置tomcat禁止不安全HTTP方法
Spring boot 内置tomcat禁止不安全HTTP方法 在tomcat的web.xml中可以配置如下内容,让tomcat禁止不安全的HTTP方法 <security-constraint ...