1. runtime 就是运行时, 运行时就说动态调用
2. OC就是运行时机制, 也就是在运行的时候的一些机制, 其中最主要的是消息机制
3. 对应C语言, 函数的调用在编译的时候就决定调用哪个函数, 对于OC, 函数的调用属于动态调用过程, 在编译的时候不能真正决定调用哪个函数, 只有在正在运行的时候才会根据函数的名称找到对于的函数来调用
4. 编译阶段, OC可以调用任意函数, 即使某个函数只有声明没有实现, 但是C中就会报错
什么是消息机制?
OC 方法的调用就是发送消息
runtime是一套比较底层的纯C语言API, 属于1个C语言库, 包含了很多底层的C语言API。 在我们平时编写的OC代码中, 程序运行过程时, 其实最终都是转成了runtime的C语言代码, runtime算是OC的幕后工作者
//注: Xcode6 开始苹果就不推荐使用runtime了, 把很多函数的参数注释了, 这样是为了让用户更加依赖自己, Xcode7 以后有很多私有的成员变量都被屏蔽了
runtime使用场景
runtime是属于OC的底层, 可以进行一些非常底层的操作(用OC是无法现实的, 不好实现)
在程序运行过程中, 动态创建一个类(比如KVO的底层实现)
在程序运行过程中, 动态地为某个类添加属性方法, 修改属性值方法
遍历一个类的所有成员变量(属性)所有方法
具体用法:
1. 如果想调用一个没有暴露出来的方法(没有在.h文件中声明的方法), 或者系统的方法
2. 写自己框架
runtime 的作用
- 发送消息
1. 导入头文件
#import <objc/message.h> 或者<objc/runtime.h>
2. Xcode编译设置
工程 --> TARGETS --> build Settings --> 搜索 msg --> 将严格检查OC消息机制 的值设置为NO
3.
Person *p = [Person alloc];
=> Person *p = objc_msgSend([Person class] @selector(alloc));
p = [p init];
=> objc_msgSend(@selector(init));
[p eat];
objc_msgSend(p, @selector(eat));
4. 方法调用的流程: (方法调用的本质是发送消息)
1. 寻找方法, (对象方法保存在类里,每个类里都有一个方法列表), 根据isa指针去找到对应的类
2. 根据方法编号去方法列表里找到对应的方法, @selector(eat)返回的就是方法编号
3. 调用方法即可
- 交换方法
有时候需要给系统的方法添加功能, 这是不能通过子类和分类来实现的功能,此时需要交换方法,步骤如下
1. 添加一个分类
2. 在分类中提供一个带功能的方法
3. 把这个方法的实现跟系统方法交换
例子:在系统的UIImage类的imageNamed:方法中添加功能(添加一个判断是否有图片的功能, 系统自己的imageNamed:方法是不具备判断功能的,如果没有UIImage实例对象就会报错)
//1. 创建分类:UIImage+img
UIImage+img.m 中 ===============>
//只会在类加载进内存的时候就会调用这个方法(load方法),(最开始的时候,程序一起的就调用,且调用一次,所以可以在这里实现方法交换)
+ (void)load {
//获取方法
Method imageNamedMethod = class_getClassMethod(self, @selector(imageNamed:));
Method kaka_imageNamedMethod = class_getClassMethod(self, @selector(kaka_imageNamed:));
//第一个参数是需要获取哪个类的方法,第二个参数是哪个方法
//方法交换的实现(将上面获取的两个方法交换一下)
method_exchangeImplementations(imageNamedMethod, kaka_imageNamedMethod)
}
+(UIImage *)kaka_imageNamed:(NSString *)name {
UIImage *img = [UIImage kaka_imageNamedMethod: name];
if (!img) {
NSLog(@"图片不存在");
}
return img;
}
//2. 导入分类, 调用方法
通过runtime实现当我调用imageNamed:就调用kaka_imageNamed:方法
在ViewController.m文件中 ===============>
在ViewDidLoad方法中直接调用系统的imageNamed:方法就可以了, 因为我们已经交换了imageNamed:方法和kaka_imageNamed方法;
- 动态添加方法
//需要动态添加的方法
self 方法调用者
_cmd 当前调用方法的编号
void eat(id self, SEL _cmd) {
NSLog(@"动态添加吃东西");
}
//只有调用了一个不存在的对象方法就会调用下面的方法, 该方法的作用是处理为实现的方法,
+ (BOOL)resolveInstanceMethod:(SEL)sel {
if (sel == NSSelectorFromString:(@"eat")) {
//注: eat是Person类中的对象方法, 但是只有声明没有实现, 在实现动态添加eat方法之前要先导入: #import <objc/message.h>
//动态添加eat方法
class_addMethod(self, sel, eat, "v@:");
//参数1: 给需要动态添加方法的类, 参数2: 需要添加的方法, 参数3: 方法的实现, 参数4: 方法的类型()
return YES;
}
return [super resolveInstanceMethod:sel];
}
动态添加方法的使用场景: 电商类应用的会员机制, 会员有一些独有的方法, 这些方法可以通过动态添加方法的方法来优化App
- 给分类添加属性
为什么要动态添加属性? 动态添加属性就是让一个属性关联某个对象, 设置属性的本质就是添加关联让这个属性与某个对象产生关联
给系统类添加属性的时候只能用runtime
以给NSObject类添加属性name为例:
1. 创建系统类的分类
2. 在.h文件中声明一个属性
@property NSString *name;
//在分类中定义的属性是没有set/get方法实现的更不会生成 _name 属性
3. 在.m文件中
#import <objc/message.h>
- (void)setName:(NSString *)name {
//参数1: 给哪个对象添加属性, 参数2: 属性名, 参数3: 属性值, 参数4: 策略(就是strong, nonatomic, 等)
objc_setAssociatedObject(self, @"name", name, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (void)name {
return objc_getAssociatedObject(self, @"name");
}
- 字典转模型
用处: 自动生成属性代码