《目录-iOS & OpenGL & OpenGL ES & Metal》
对象和类之前已经分析过了,今天来分析一下方法,说到方法就不得不探究一下Runtime了~
一、Runtime
1、Runtime的定义
Runtime
是一套API。
详细来说,是一套由c、c++、汇编
一起编写而成,并为oc代码提供运行时功能的api。
为什么要这么费劲巴拉的搞这么一套东西,不直接用oc呢?
- 因为oc对于计算机来说是一门高级语言,而c、c++、汇编相比oc,更加稳定,执行效率也更快。总体来说是为了稳定性和高效率!
运行时 & 编译时
-
运行时:代码跑起来的时间,代码会被装载在内存
-
编译时:正在编译的时间,将源代码翻译成可识别的机器语言(如:二进制)
2、Runtime的版本
- 老版本,
legacy version
,OC 1.0
,__OBJC__
,-old
- 新版本,
modern version
,OC 2.0
,__OBJC2__
,-new
3、Runtime的使用
运行时动态库 Runtime System Library
通过 编译器 compiler
编译之后,提供了 Framework&Service
和 Runtime API
供OC使用。
我们经常在OC中调用@selector()
,其实就是在和runtime打交道了
- OC上层方法 :
@selector()
- NSObject方法 :
NSSelectorFromName
- Runtime 底层api:
sel_registerName
二、方法的本质
1、准备工作
测试代码:
// Person是继承自NSObject 自定义的类
Person *person = [Person alloc];
[person sayHello];
//定义一个void fly() {···},直接调用方法
fly();
通过clang手段:
兼容编译(代码少):clang -rewrite-objc main.m -o main.cpp
完整编译(不报错):xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m -o main.cpp
2、方法的底层编译
看一下底层编译成什么:
Person *person = ((Person *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("Person"), sel_registerName("alloc"));
((void (*)(id, SEL))(void *)objc_msgSend)((id)person, sel_registerName("sayHello"));
fly();
解析:
((Person *(*)(id, SEL))(void *)objc_msgSend)
和((void (*)(id, SEL))(void *)objc_msgSend)
都是 类型强转(id)objc_getClass("Person")
拿到Person类sel_registerName("alloc")
和sel_registerName("sayHello")
都是 调用方法,好比@selector()
- 定义的C函数
fly()
在clang
后,并没有编译成objc_msgSend
函数去调用。因为发送消息就是查找函数实现的过程,C函数可以直接通过函数名(指针)找到,并不需要这一步。
重点即:objc_msgSend(id,sel)
= 发送消息(消息接收者,方法编号)
3、方法的本质
由底层编译可以看出,我们调用方法,在底层会变成objc_msgSend
函数的调用。也就是说:
方法的本质就是发送消息
!
4、给不同的接受者发消息
假定条件: 有一个子类 Person,实例方法:run ,类方法:jump 有一个父类 People,实例方法:sayHi,类方法:sayBey
4.1 给对象发消息
//person的角度
objc_msgSend(person, sel_registerName("run"));
4.2 给类发消息
//person的角度
objc_msgSend(objc_getClass("Person"), sel_registerName("jump"));
4.3 给父类发消息(实例方法)
给父类发消息,要用另一个函数
objc_msgSendSuper
,并且要用到objc_super
这个结构体
struct objc_super {
//只看oc2版本需要的2个参数
__unsafe_unretained _Nonnull id receiver;
//···
__unsafe_unretained _Nonnull Class super_class;
//···
};
//person 的角度
struct objc_super mySuper;
mySuper.receiver = person;
mySuper.super_class = [Person class];
objc_msgSendSuper(&mySuper, @selector(sayHi));
4.3 给父类发消息(类方法)
//person 的角度
struct objc_super myClassSuper;
myClassSuper.receiver = [Person class];
myClassSuper.super_class = class_getSuperclass(object_getClass([Person class]));// 元类
objc_msgSendSuper(&myClassSuper, sel_registerName("sayBey"));