1.基本概念
编译时与运行时
源代码转换为可执行的程序,通常需要经过三个步骤:编译、链接、运行,不同的编译语言,这三个步骤中所进行的操作又有些不同。
编译时就是正在编译的时候,即编译器将源代码翻译成机器能识别的代码的过程。编译时知识对语言进行最基本的检查报错,包括词法分析、语法分析等,编译通过并不意味着程序就可以成功运行。
运行时就是程序通过编译后,编译好的代码被装载到内存中跑起来的阶段,这个时候会具体对类型进行检查,而不仅仅是对代码简单扫描分析,此时如果出错,程序会崩溃。
静态语言与动态语言
静态语言就是在编译阶段就已经确定了所有变量的数据类型,同时也确定好了要调用的函数,以及函数的实现,如C语言。
动态语言在编译阶段并不知道变量的具体数据类型,也不知道真正调用的哪个函数。只有在运行期间才检查变量的数据类型,同时在运行时才会根据函数名查找要调用的具体函数。程序没运行的时候,并不知道调用一个方法具体会发生什么。如Objective-C语言。
Objective-C将一些决定性的工作从编译阶段、链接阶段推迟到运行时阶段的机制,使得Objective-C变得更加灵活。甚至可以在程序运行的时候,动态的去修改一个方法的实现,为“热更新”机制提供可能性。
而实现Objective-C语言运行时机制的一切基础就是Runtime,Runtime实际上就是一个库,这个库可以在程序运行时动态的创建对象、检查对象、修改类和对象的方法。
2.方法调用机制
Objective-C作为扩展于C语言的一种面向对象的编程语言,然而其方法的调用方式又和大多数面向对象语言大有不同,其采用消息传递、转发的方式进行方法的调用。这种机制使得Objective-C中对象的真正行为往往在运行时确定而非在编译时确定,所以被称为运行时动态语言。
在Objective-C语言中采用中括号包裹的方式,例如[obj function]进行方法的调用。实际上,Objective-C中的每一句方法调用最后都会被转换成一条消息进行发送。一条消息包含三部分内容:方法选择器、接收消息的对象以及参数。obj_msgSend函数就用来发送这种消息。
MyObject.m
#import "MyObject.h"
@implementation MyObject
- (void)showSelf:(NSString *)name age:(int)age{
NSLog(@"MyObject:%@,%d",name,age);
}
@end
main.m
#import <UIKit/UIKit.h>
#import "AppDelegate.h"
#import "MyObject.h"
#import "objc/message.h"
int main(int argc, char * argv[]) {
@autoreleasepool {
MyObject *obj = [[MyObject alloc] init];
[obj class];
#pragma clang diagnostic push
#pragma clang diagnostic ignored"-Wundeclared-selector"
// ((void(*)(id,SEL))objc_msgSend)(obj,@selector(showSelf));
((void(*)(id,SEL,NSString*,int))objc_msgSend)(obj,@selector(showSelf:age:),@"abc",22);
#pragma clang diagnostic pop
}
return 0;
}
如上可知,通过@selector(方法名)可以获取到一个SEL类型的对象,SEL实际上是object_selector结构体指针,也可以将其理解为函数签名,在程序的编译阶段,定义类中所有方法会生成一个方法签名列表,这个列表是类直接关联的,在运行时通过方法签名表来找到具体要执行的函数。
再看上例使用到的objc_msgSend()函数,其第一个参数为接收消息的对象,第二个参数为方法签名,之后为传递的参数,那么Objective-C运行时是如何根据一个对象实例来找到方法签名表再找到要执行的方法呢?此处则需要理解相关的内部构造
3.Runtime概念解析
1.Class(类)
在Runtime中,Class被定义为指向objc_class结构体的指针,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;