什么是runtime
我们都知道,将源代码转换为可执行的程序,通常要经过三个步骤:编译、链接、运行。
C 语言 作为一门静态类语言,在编译阶段就已经确定了所有变量的数据类型,同时也确定好了要调用的函数,以及函数的实现。
而 Objective-C 语言 是一门动态语言。在编译阶段并不知道变量的具体数据类型,也不知道所真正调用的哪个函数。只有在运行时间才检查变量的数据类型,同时在运行时才会根据函数名查找要调用的具体函数。
而实现 Objective-C 语言 运行时机制的一切基础就是 Runtime。
Runtime 实际上是一个库,这个库使我们可以在程序运行时动态的创建对象、检查对象,修改类和对象的方法。
runtime中的概念解析
runtime中的消息发送机制在“消息传递与消息转发”博客中详细讲解过,因此这里直接贴博客链接,不多赘述:iOS——消息传递和消息转发
Class(类)
在runtime中,类被定义为指向objc_class结构体的指针。结构体数据结构:
/// An opaque type that represents an Objective-C class.
typedef struct objc_class *Class;
struct objc_class {
Class _Nonnull isa; // objc_class 结构体的实例指针
#if !__OBJC2__
Class _Nullable super_class; // 指向父类的指针
const char * _Nonnull name; // 类的名字
long version; // 类的版本信息,默认为 0
long info; // 类的信息,供运行期使用的一些位标识
long instance_size; // 该类的实例变量大小;
struct objc_ivar_list * _Nullable ivars; // 该类的实例变量列表
struct objc_method_list * _Nullable * _Nullable methodLists; // 方法定义的列表
struct objc_cache * _Nonnull cache; // 方法缓存
struct objc_protocol_list * _Nullable protocols; // 遵守的协议列表
#endif
};
objc_class结构体存放的数据称为元数据
objc_class 结构体 的第一个成员变量是 isa 指针,isa 指针 保存的是所属类的结构体的实例的指针,这里保存的就是 objc_class 结构体的实例指针,而实例换个名字就是对象。换句话说,Class(类) 的本质其实就是一个对象,我们称之为类对象。
object(对象)
在“objc/objc.h”中,Object(对象)被定义为 objc_object 结构体,其数据结构如下:
/// Represents an instance of a class.
struct objc_object {
Class _Nonnull isa; // objc_object 结构体的实例指针
};
/// A pointer to an instance of a class.
typedef struct objc_object *id;
这里的 id 被定义为一个指向 objc_object 结构体 的指针。从中可以看出 objc_object 结构体 只包含一个 Class 类型的 isa 指针。
换句话说,一个 Object(对象)唯一保存的就是它所属 Class(类) 的地址。当我们对一个对象,进行方法调用时,比如 [receiver selector];,它会通过 objc_object 结构体的 isa 指针 去找对应的 objc_class 结构体,然后在 objc_class 结构体 的 methodLists(方法列表) 中找到我们调用的方法,然后执行。
Meta Class(元类)
Meta Class(元类) 就是一个类对象所属的 类。一个对象所属的类叫做 类对象,而一个类对象所属的类就叫做 元类。
Runtime 中把类对象所属类型就叫做 Meta Class(元类),用于描述类对象本身所具有的特征,而在元类的 methodLists 中,保存了类的方法链表,即所谓的「类方法」。并且类对象中的 isa 指针 指向的就是元类。每个类对象有且仅有一个与之相关的元类。
我们是通过对象的 isa 指针 找到 对应的 Class(类);然后在 Class(类) 的 method list(方法列表) 中找对应的 selector 。
而 类方法的调用过程 和对象方法调用差不多,流程如下:
通过类对象 isa 指针 找到所属的 Meta Class(元类);
在 Meta Class(元类) 的 method list(方法列表) 中找到对应的 selector;
执行对应的 selector。
比如这段代码:
NSString *testString = [NSString stringWithFormat:@"%d,%s",3, "test"];
上边的示例中,stringWithFormat: 被发送给了 NSString 类,NSString 类 通过 isa 指针 找到 NSString 元类,然后在该元类的方法列表中找到对应的 stringWithFormat: 方法,然后执行该方法。
实例对象、类、元类之间的关系
方法(method)
objc_class 结构体 的 methodLists(方法列表)中存放的元素就是 方法(Method)。
先看runtime中objc_method结构体的数据结构:
/// An opaque type that represents a method in a class definition.
/// 代表类定义中一个方法的不透明类型
typedef struct objc_method *Method;
struct objc_method {
SEL _Nonnull method_name; // 方法名
char * _Nullable method_types; // 方法类型
IMP _Nonnull method_imp; // 方法实现
};
- 首先来看方法名:SEL 是一个指向 objc_selector 结构体 的指针。SEL的意思同样在“消息传递与消息转发”的博客中有讲:iOS——消息传递和消息转发
- 其次是方法实现:IMP 的实质是一个函数指针,所指向的就是方法的实现。IMP用来找到函数地址,然后执行函数。IMP在“消息传递与消息转发”的博客中有讲:iOS——消息传递和消息转发
- 方法类型:方法类型 method_types 是个字符串,用来存储方法的参数类型和返回值类型。
因此,Method 就是将 SEL(方法名) 和 IMP(函数指针) 关联起来,当对一个对象发送消息时,会通过给出的 SEL(方法名) 去找到 IMP(函数指针) ,然后执行。
总结
runtime中主要还是涉及消息传递和消息转发的内容居多,因此想要更深入的了解runtime,可以再去看我的“消息传递和消息转发”这篇博客学习:iOS——消息传递和消息转发