楔子
Runtime是什么?见名知意,其概念无非就是“因为 Objective-C 是一门动态语言,所以它需要一个运行时系统……这就是 Runtime 系统”云云。对博主这种菜鸟而言,Runtime 在实际开发中,其实就是一组C语言的函数。胡适说:“多研究些问题,少谈些主义”,云山雾罩的概念听多了总是容易头晕,接下来我们直接从代码入手学习 Runtime。
1、由objc_msgSend说开去:
Objective-C 中的方法调用,不是简单的方法调用,而是发送消息,也就是说,其实 [receiver message] 会被编译器转化为: objc_msgSend(receiver, selector),何以证明?新建一个类 MyClass,其.m文件如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
#import "MyClass.h" @implementation MyClass -(instancetype)init{ if (self = [super init]) { [self showUserName]; } return self; } -(void)showUserName{ NSLog(@"Dave Ping"); } |
使用 clang 重写命令:
1 |
$ clang -rewrite-objc MyClass.m |
然后在同一目录下会多出一个 MyClass.cpp 文件,双击打开,可以看到 init 方法已经被编译器转化为下面这样:
1 2 3 4 5 6 |
static instancetype _I_MyClass_init(MyClass * self, SEL _cmd) { if (self = ((MyClass *(*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)((__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass("MyClass"))}, sel_registerName("init"))) { ((void (*)(id, SEL))(void *)objc_msgSend)((id)self, sel_registerName("showUserName")); } return self; } |
我们要找的就是它:
1 |
((void (*)(id, SEL))(void *)objc_msgSend)((id)self, sel_registerName("showUserName")) |
objc_msgSend 函数被定义在 objc/message.h 目录下,其函数原型是酱紫滴:
1 |
OBJC_EXPORT void objc_msgSend(void /* id self, SEL op, ... */ ) |
该函数有两个参数,一个 id 类型,一个 SEL 类型。
2、SEL
SEL 被定义在 objc/objc.h 目录下:
1 |
typedef struct objc_selector *SEL; |
其实它就是个映射到方法的C字符串,你可以用 Objective-C 编译器命令 @selector() 或者 Runtime 系统的 sel_registerName 函数来获得一个 SEL 类型的方法选择器。
3、id
与 SEL 一样,id 也被定义在 objc/objc.h 目录下:
1 |
typedef struct objc_object *id; |
id 是一个结构体指针类型,它可以指向 Objective-C 中的任何对象。objc_object 结构体定义如下:
1 |
struct objc_object { Class isa OBJC_ISA_AVAILABILITY;}; |
我们通常所说的对象,就长这个样子,这个结构体只有一个成员变量 isa,对象可以通过 isa 指针找到其所属的类。isa 是一个 Class 类型的成员变量,那么 Class 又是什么呢?
4、Class
Class 也是一个结构体指针类型:
1、由objc_msgSend说开去:
Objective-C 中的方法调用,不是简单的方法调用,而是发送消息,也就是说,其实 [receiver message] 会被编译器转化为: objc_msgSend(receiver, selector),何以证明?新建一个类 MyClass,其.m文件如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
#import "MyClass.h" @implementation MyClass -(instancetype)init{ if (self = [super init]) { [self showUserName]; } return self; } -(void)showUserName{ NSLog(@"Dave Ping"); } |
使用 clang 重写命令:
1 |
$ clang -rewrite-objc MyClass.m |
然后在同一目录下会多出一个 MyClass.cpp 文件,双击打开,可以看到 init 方法已经被编译器转化为下面这样:
1 2 3 4 5 6 |
static instancetype _I_MyClass_init(MyClass * self, SEL _cmd) { if (self = ((MyClass *(*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)((__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass("MyClass"))}, sel_registerName("init"))) { ((void (*)(id, SEL))(void *)objc_msgSend)((id)self, sel_registerName("showUserName")); } return self; } |
我们要找的就是它:
1 |
((void (*)(id, SEL))(void *)objc_msgSend)((id)self, sel_registerName("showUserName")) |
objc_msgSend 函数被定义在 objc/message.h 目录下,其函数原型是酱紫滴:
1 |
OBJC_EXPORT void objc_msgSend(void /* id self, SEL op, ... */ ) |
该函数有两个参数,一个 id 类型,一个 SEL 类型。
2、SEL
SEL 被定义在 objc/objc.h 目录下:
1 |
typedef struct objc_selector *SEL; |
其实它就是个映射到方法的C字符串,你可以用 Objective-C 编译器命令 @selector() 或者 Runtime 系统的 sel_registerName 函数来获得一个 SEL 类型的方法选择器。
3、id
与 SEL 一样,id 也被定义在 objc/objc.h 目录下:
1 |
typedef struct objc_object *id; |
id 是一个结构体指针类型,它可以指向 Objective-C 中的任何对象。objc_object 结构体定义如下:
1 |
struct objc_object { Class isa OBJC_ISA_AVAILABILITY;}; |
我们通常所说的对象,就长这个样子,这个结构体只有一个成员变量 isa,对象可以通过 isa 指针找到其所属的类。isa 是一个 Class 类型的成员变量,那么 Class 又是什么呢?
4、Class
Class 也是一个结构体指针类型: