runtime
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");
}
- 字典转模型
用处: 自动生成属性代码