神奇的Runtime
做iOS开发的都知道,OC的所有代码, 最终都会转化成为运行时语言让计算机去理解运行
底层我这里不深究,那么这个每次面试官挂在嘴边的这个runtime到底是个什么鬼
今天花了一点时间 来初步了解一下runtime吧
这里先引用一篇大神的文章《OC最实用的runtime总结》
文章主要阐述的几个使用案例和场景.
1.Runtime_ClassMethodAndImplementationsMethodDeals - 获取类方法和实例方法,并且交换方法
2.Runtime_ReplaceSystemMethod - 拦截系统方法,并且替换
3.Runtime_CategoryAddAttribute - 给分类增加属性
4.Runtime_NSCoding - Runtime归档解档方法,不必一个一个添加属性
5.Runtime_MJExtensionBase - 字典转模型(MJExtension的基础原理)
基础方法说明
- 获得某个类的类方法
Method class_getClassMethod(Class cls , SEL name)
- 获得某个类的实例对象方法
Method class_getInstanceMethod(Class cls , SEL name)
- 交换两个方法的实现
void method_exchangeImplementations(Method m1 , Method m2)
- 获得某个类的所有成员变量(outCount 会返回成员变量的总数) 参数: 1、哪个类 2、放一个接收值的地址,用来存放属性的个数 3、返回值:存放所有获取到的属性,通过下面两个方法可以调出名字和类型
Ivar *class_copyIvarList(Class cls , unsigned int *outCount)
- 获得成员变量的名字
const char *ivar_getName(Ivar v)
- 获得成员变量的类型
const char *ivar_getTypeEndcoding(Ivar v)
针对新手
我现在能想到用的就是这两个比较适用 理解起来也不复杂,在一些框架里面也比较常用,有助于你去读代码~
Runtime_ReplaceSystemMethod - 拦截系统方法,并且替换
,
Runtime_ClassMethodAndImplementationsMethodDeals - 获取类方法和实例方法,并且交换方法
案例1:方法简单的交换
创建一个Person类,类中实现以下两个类方法,并在.h 文件中声明
+ (void)run {
NSLog(@"跑");
}
+ (void)study {
NSLog(@"学习");
}
控制器中调用,则先打印跑,后打印学习
[Person run];
[Person study];
下面通过runtime 实现方法交换,类方法用class_getClassMethod ,对象方法用class_getInstanceMethod
// 获取两个类的类方法
Method m1 = class_getClassMethod([Person class], @selector(run));
Method m2 = class_getClassMethod([Person class], @selector(study));
// 开始交换方法实现
method_exchangeImplementations(m1, m2);
// 交换后,先打印学习,再打印跑!
[Person run];
[Person study];
案例2:拦截系统方法(非常实用)
需求:比如iOS6 升级 iOS7 后需要版本适配,根据不同系统使用不同样式图片(拟物化和扁平化),如何通过不去手动一个个修改每个UIImage的imageNamed:方法就可以实现为该方法中加入版本判断语句?
步骤: 1、为UIImage建一个分类(UIImage+Category) 2、在分类中实现一个自定义方法,方法中写要在系统方法中加入的语句,比如版本判断
+ (UIImage *)xh_imageNamed:(NSString *)name {
double version = [[UIDevice currentDevice].systemVersion doubleValue];
if (version >= 7.0) {
// 如果系统版本是7.0以上,使用另外一套文件名结尾是‘_os7’的扁平化图片
name = [name stringByAppendingString:@"_os7"];
}
return [UIImage xh_imageNamed:name];
}
3、分类中重写UIImage的load方法,实现方法的交换(只要能让其执行一次方法交换语句,load再合适不过了)
+ (void)load {
// 获取两个类的类方法
Method m1 = class_getClassMethod([UIImage class], @selector(imageNamed:));
Method m2 = class_getClassMethod([UIImage class], @selector(xh_imageNamed:));
// 开始交换方法实现
method_exchangeImplementations(m1, m2);
}
注意:自定义方法中最后一定要再调用一下系统的方法,让其有加载图片的功能,但是由于方法交换,系统的方法名已经变成了我们自定义的方法名(有点绕,就是用我们的名字能调用系统的方法,用系统的名字能调用我们的方法),这就实现了系统方法的拦截!
利用以上思路,我们还可以给 NSObject 添加分类,统计创建了多少个对象,给控制器添加分类,统计有创建了多少个控制器,特别是公司需求总变的时候,在一些原有控件或模块上添加一个功能,建议使用该方法!
关于 objc关联
(在一套代码中看到作者,一直使用objc_setAssociatedObject ,所以摘出来研究一下)
关联是指把两个对象相互关联起来,使得其中的一个对象作为另外一个对象的一部分。
objc_setAssociatedObject、objc_getAssociatedObject、objc_removeAssociatedObjects
使用关联
我们可以不用修改类的定义而为其对象增加存储空间。这在我们无法访问到类的源码的时候或者是考虑到二进制兼容性的时候是非常有用。
创建关联
创建关联要使用到Objective-C的运行时函数:objc_setAssociatedObject来把一个对象与另外一个对象进行关联。该函数需要四个参数:源对象,关键字,关联的对象和一个关联策略。
//#import 头文件
//objc_setAssociatedObject需要四个参数:源对象,关键字,关联的对象和一个关联策略。
//1 源对象alert
//2 关键字 唯一静态变量key associatedkey
//3 关联的对象 sender
//4 关键策略 OBJC_ASSOCIATION_ASSIGN
使用示例
- btn 多参数传值
UIButton *btn = [UIButton new];
[btn setBackgroundColor:[UIColor greenColor]];
btn.frame = CGRectMake(100, 100, 100, 100);
[self.view addSubview:btn];
NSArray *arr = @[@"呵呵",@"哈哈"];
objc_setAssociatedObject(btn, &overviewKey, arr, OBJC_ASSOCIATION_RETAIN);
[btn addTarget:self action:@selector(btnClick:) forControlEvents:UIControlEventTouchUpInside];
- (void)btnClick:(UIButton *)btn{
NSArray *arr = objc_getAssociatedObject(btn, &overviewKey);
NSLog(@"%@",arr[0]);
}
//这时点击按钮 就会发现这个数组已经跟按钮相关联了
注意事项
关联是基于关键字的,因此,我们可以为任何对象增加任意多的关联,每个都使用不同的关键字即可。关联是可以保证被关联的对象在关联对象的整个生命周期都是可用的(在垃圾自动回收环境下也不会导致资源不可回收)。
在上边例子里面使用的策略关键字需要要提前定义如下
static char overviewKey
;
PS : 嗯 小白暂时就理解了这么多了, 喜欢的记得点赞 再有收获了会继续分享哦~