运行时。OC是运行时机制,也就是在运行的时候的一些机制,其中最主要的是消息机制。
对于C语言,函数的调用在编译的时候会决定调用哪个函数。
而对于OC语言中的函数,属于动态调用过程,在编译的时候并不能决定真正调用哪个函数,只有在真正运行的时候才会根据函数的名称找到对应的函数来调用。
在编译阶段,C语言调用未实现的函数会报错。
在编译阶段,OC可以调用任何函数,即使这个函数并未实现,只要声明过就不会报错。
runtime最主要的有五大作用
发送消息(访问私有函数)
交换方法
动态添加方法
给分类添加属性
字典转模型
1.发送消息(访问私有函数)
方法调用的本质就是让对象发送消息,OC底层实现通过runtime实现发送消息。
最终生成消息机制,是编译器要做的事情。
可以使用clangclang -rewrite-objc main.m 将当前代码进行重新编译,并查看最终生成代码。
NSObject *obj = [NSObject alloc];
obj = [obj init];
最终会转换为
NSObject *obj = ((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("NSObject"), sel_registerName("alloc"));
obj = ((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)obj, sel_registerName("init"));
简化后,可以变为
NSObject *obj = objc_msgSend(objc_getClass("NSObject"), sel_registerName("alloc"));
obj = objc_msgSend(obj, sel_registerName("init"));
其中
objc_msgSend发送消息
objc_getClass获取发送消息的主体
sel_registerName发送消息的编号
当然,我们在使用的时候,不用上面的复杂格式,一般我们可以这样使用:
NSObject *obj = objc_msgSend([NSObject class], @selector(alloc));
obj = objc_msgSend(obj, @selector(init));
使用runtime的基本步骤:
- 在文件中引入头文件:
#import <objc/message.h>
- 将build settings 里面的objc_msgSend修改为NO(xcode6之前苹果可以使用runtime,xcode6之后苹果不推荐使用runtime)
那,在什么情况下,我们会使用runtime的发送消息机制呢?
一般在,我们需要访问一个私有函数的时候,可以使用runtime
方法调用流程:
[person eat];是怎样的一个调用过程?
首先要明白,对象方法列表是在类对象里面。类方法列表是在元类里面。
在person对象中通过isa指针找到person的类对象YZPerson
注册方法编号:sel_registerName(“eat”)
根据方法编号去方法列表里面查找对应的方法
找到方法地址去方法区调用函数的具体实现
2. 交换方法
交换类方法
交换对象方法
交换图
交换的其实是,方法列表到方法实现。
3. 动态添加方法
4.给分类添加属性
@property在分类中的作用:只会添加set、get方法的声明,并不会做实现。也不会生成下划线成员属性。