Runtime 的一些用法
2016-10-14 18:12
162 查看
runtime 就是OC 中经常说的 运行时
这里 简单介绍一下 OC 中用到一些场景
1、字典转模型
2、给分类添加关联对象
3、交换方法
runtime 使用的时候一般建立一个 NSObject 的分类Cotegory 。 当然也可以根据 实际情况创建其他类的Cotegory。在创建的文件里面需要导入
unsigned int count =0;
//下面为数组
objc_property_t *proList = class_copyPropertyList([selfclass],
&count);
注意,这个方法获取的数组最后要用 free(proList); 释放掉
class_copyPropertyList
获取 类的属性的列表
class_copyIvarList
获取类的 成员变量的列表
class_copyMethodList
获取类 的方法的列表
class_copyProtocolList
获取类的代理的列表
参数
1、 要获取的这个类 因为是Cotegory 分类 ,使用 self
2、列表里面元素的个数 unsigned int 无符号的整形
返回值:
所有属性的数组 类型为 objc_property_t 的数组
再遍历类的属性数组 ,因为类型为 objc_property_t 是一个指针,使用property_getName获取这个指针对应属性的名字。
使用property_getName 获取的是一个const char 类型C 语言的字符串 再转化为OC的字符串 。最后添加到存储数据的数组里面。
获取到类的属性名称后,就可以使用KVC 就行赋值了。
使用:
创建一个Person 类 不实现.m
在ViewController 里面
导入
#import "Person.h"
#import "NSObject+Runtime.h"
如果因为iOS 在运行的时候 里面的类的对象不会改变,重复的获取 类 的属性名称 会造成 影响APP性能,这个时候可以使用关联对象的方法。
修改的方法 里面 多了几行代码
分别在方法的开始和结尾
objc_getAssociatedObject 获取关联对象动态添加的属性值
参数:
1、对象 self
2、const void 的key
返回值:
Id类型, 添加的属性值
objc_setAssociatedObject 设置动态添加关联对象的属性值
参数:
1、对象 self
2、const void 的key 跟上面获取的key一致
3、要添加的属性值,(如果设置了,上面的objc_getAssociatedObject 就能直接获取到,下次调用就能直接获取属性值)
4、关联的协议 OBJC_ASSOCIATION_RETAIN_NONATOMIC
添加了 了关联对象 的方法,下次调用该方法的时候,运行到objc_getAssociatedObject 就能直接获取到 数据了,不用再往下执行了。
在无法修改系统方法 ,和第三方框架的时候,
1、利用交换方法,先执行自定义的方法
2、再执行系统方法或第三方框架方法。
被称之为 黑魔法 ,对系统和框架有很强的依赖性。
举个例子:
在UIImageView 的时候,系统的设置图片的方法, imageView.image = [UIImage imageNamed:@""];
imageView.image 相当于 imageView setImage: UIImage
我们可以自定义一个方法,替换掉系统的setImage: 方法
创建一个UIImageView 的Cotegory
首先先自定义一个设置图片的方法- (void) cz_setImage:(UIImage *)image
里面 输出 当前的方法。
在方法 + (void) load{} 里面添加 我们的交换方法
就3句话,
1、获取系统默认的 设置图片的方法
2、获取自定义的设置图片的方法
3、交换这两个方法
class_getInstanceMethod 获取类的实例的方法
参数:
1、类的实例 [self class] (这是一种特殊的实例化对象)
2、获取的实例方法 SEL
返回值:
Method 方法 类型
method_exchangeImplementations
参数:
1、要交换的第一个方法
2、要交换的第二个方法
重点注意:
在方法交换之后,两个方法已经被交换了,调用系统方法 变成调用自定义的方法名
调用自定义的方法 变成调用系统的方法名
所以下面:
ViewController 的运行结果为
#import "UIImageView+RuntimeCrossoverMethod.h"
2016-10-14 16:38:01.248 Runtime[4947:837294]调用的自定义的方法: -[UIImageView(RuntimeCrossoverMethod) cz_setImage:]
我们调用系统默认的方法 变成调用我们自定义的方法,当然我们在自定义方法里面又掉用了系统的方法(这个时候系统的方法名变成了我们自定义的方法名)。
这里 简单介绍一下 OC 中用到一些场景
1、字典转模型
2、给分类添加关联对象
3、交换方法
runtime 使用的时候一般建立一个 NSObject 的分类Cotegory 。 当然也可以根据 实际情况创建其他类的Cotegory。在创建的文件里面需要导入
#import <objc/runtime.h>
一、字典转模型。
首先 动态的获取 类 的属性名称 -> 然后使用 KVC 进行赋值.1、首先获取类的属性名称
.h/** 获取 属性的名称数组 @return 返回对象的属性名称数组 */ + (NSArray *)cz_objPropertiesAry;.m
const void *kPropertiesKey = "kPropertiesKey"; + (NSArray *)cz_objPropertiesAry { //获取属性数组的指针数组 unsigned int count = 0; objc_property_t *property = class_copyPropertyList([self class], &count); //创建存放数据的数组 NSMutableArray *array = [NSMutableArray array]; //遍历属性数组 for (unsigned int i = 0; i < count; i++) { // 指针 C语言中数组的名字 为第一个元素的指针 objc_property_t pty = property[i]; //获取属性的名字 类型为C的字符串 const char *cName = property_getName(pty); //把C字符串转化为OC字符串 NSString *name = [NSString stringWithCString:cName encoding:NSUTF8StringEncoding]; //[NSString stringWithUTF8String:cName]; 也可以 //把信息添加到数组里面 [array addObject:name]; } //释放属性数组 free(property); return array.copy; }
unsigned int count =0;
//下面为数组
objc_property_t *proList = class_copyPropertyList([selfclass],
&count);
注意,这个方法获取的数组最后要用 free(proList); 释放掉
class_copyPropertyList
获取 类的属性的列表
class_copyIvarList
获取类的 成员变量的列表
class_copyMethodList
获取类 的方法的列表
class_copyProtocolList
获取类的代理的列表
参数
1、 要获取的这个类 因为是Cotegory 分类 ,使用 self
2、列表里面元素的个数 unsigned int 无符号的整形
返回值:
所有属性的数组 类型为 objc_property_t 的数组
再遍历类的属性数组 ,因为类型为 objc_property_t 是一个指针,使用property_getName获取这个指针对应属性的名字。
使用property_getName 获取的是一个const char 类型C 语言的字符串 再转化为OC的字符串 。最后添加到存储数据的数组里面。
获取到类的属性名称后,就可以使用KVC 就行赋值了。
//所有字典转模型框架 核心算法 + (instancetype)cz_objcWithDictionary:(NSDictionary *)dict { //实例化对象 id object = [[self alloc]init]; //获取对象的属性名称数组 //1> 获得 self 的属性列表 NSArray *array = [self cz_objPropertiesAry]; //遍历字典 使用KVC 为数组中的属性赋值 [dict enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id _Nonnull obj, BOOL * _Nonnull stop) { //需要先判断数组中是否包含 字典的Key if ([array containsObject:key]) { //赋值 [object setValue:obj forKey:key]; } }]; return object; }
使用:
创建一个Person 类 不实现.m
#import <Foundation/Foundation.h> @interface Person : NSObject @property (nonatomic, copy) NSString *name; @property (nonatomic, assign) NSInteger age; @property (nonatomic, assign) double height; @property (nonatomic, copy) NSString *title; @end
在ViewController 里面
导入
#import "Person.h"
#import "NSObject+Runtime.h"
//获取Persion的属性列表数组 NSArray *properties = [Person cz_objPropertiesAry]; NSLog(@"%@",properties); NSDictionary *dic = @{@"name":@"张三",@"age":@(22),@"height":@(168),@"title":@"运行时",@"place":@"boss"}; Person *persion = [Person cz_objcWithDictionary:dic]; NSLog(@"%@",persion);
如果因为iOS 在运行的时候 里面的类的对象不会改变,重复的获取 类 的属性名称 会造成 影响APP性能,这个时候可以使用关联对象的方法。
二、添加关联 动态的添加属性值
修改获取类属性的方法const void *kPropertiesKey = "kPropertiesKey"; + (NSArray *)cz_objPropertiesAry { #pragma mark 关联对象 获取 属性值 没有 则添加属性值 (动态的添加属性值) /* 此方法虽然能够获取类的属性数组 但是如果每次调用都要执行一次的话 耗费的时间长, 使用 关联对象 动态的添加属性值 分别在方法的开头和结尾 */ /* 参数: 1、对象 self 2、const void 的key 返回值 : Id类型 添加的属性值 */ NSArray *proList = objc_getAssociatedObject(self, kPropertiesKey); if (proList) { //如果获取的关联对象里面有元素 就直接返回 return proList; } //获取属性数组的指针数组 unsigned int count = 0; objc_property_t *property = class_copyPropertyList([self class], &count); //创建存放数据的数组 NSMutableArray *array = [NSMutableArray array]; //遍历属性数组 for (unsigned int i = 0; i < count; i++) { // 指针 C语言中数组的名字 为第一个元素的指针 objc_property_t pty = property[i]; //获取属性的名字 类型为C的字符串 const char *cName = property_getName(pty); //把C字符串转化为OC字符串 NSString *name = [NSString stringWithCString:cName encoding:NSUTF8StringEncoding]; //[NSString stringWithUTF8String:cName]; 也可以 //把信息添加到数组里面 [array addObject:name]; } //释放属性数组 free(property); #pragma mark 关联对象 2 添加属性值 动态的添加属性值 /* 参数: 1、对象 self 2、const void 的key 同第一步 3、添加的属性值 4、关联的协议 */ objc_setAssociatedObject(self, kPropertiesKey, array.copy, OBJC_ASSOCIATION_RETAIN_NONATOMIC); return array.copy; }
修改的方法 里面 多了几行代码
NSArray *proList = objc_getAssociatedObject(self, kPropertiesKey); if (proList) { return proList; }
objc_setAssociatedObject(self, kPropertiesKey, array.copy, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
分别在方法的开始和结尾
objc_getAssociatedObject 获取关联对象动态添加的属性值
参数:
1、对象 self
2、const void 的key
返回值:
Id类型, 添加的属性值
objc_setAssociatedObject 设置动态添加关联对象的属性值
参数:
1、对象 self
2、const void 的key 跟上面获取的key一致
3、要添加的属性值,(如果设置了,上面的objc_getAssociatedObject 就能直接获取到,下次调用就能直接获取属性值)
4、关联的协议 OBJC_ASSOCIATION_RETAIN_NONATOMIC
添加了 了关联对象 的方法,下次调用该方法的时候,运行到objc_getAssociatedObject 就能直接获取到 数据了,不用再往下执行了。
三、交换方法
特点:在无法修改系统方法 ,和第三方框架的时候,
1、利用交换方法,先执行自定义的方法
2、再执行系统方法或第三方框架方法。
被称之为 黑魔法 ,对系统和框架有很强的依赖性。
举个例子:
UIImageView *imageView = [[UIImageView alloc]initWithFrame:CGRectMake(0, 0, 200, 200)]; imageView.center = self.view.center; [self.view addSubview:imageView]; imageView.image = [UIImage imageNamed:@"image5.png"];
在UIImageView 的时候,系统的设置图片的方法, imageView.image = [UIImage imageNamed:@""];
imageView.image 相当于 imageView setImage: UIImage
我们可以自定义一个方法,替换掉系统的setImage: 方法
创建一个UIImageView 的Cotegory
//在类 被加载到运行时的时候,就会执行 + (void)load{ //交叉方法 就下面的3句话 //获取 类 实例化的原始方法 setImage: Method originalMethod = class_getInstanceMethod([self class], @selector(setImage:)); //获取 自定义的类的实例化的方法 cz_setImage: Method swizzleMethod = class_getInstanceMethod([self class], @selector(cz_setImage:)); //交换两个方法 setImage: 和 cz_setImage: 完成之后 //1> 调用setImage: 相当于调用 cz_setImage: //2> 调用 cz_setImage: 相当于调用 setImage: method_exchangeImplementations(originalMethod, swizzleMethod); } //自定义 的 类 的实例化方法 Cotegory - (void)cz_setImage:(UIImage *)image { //这里是我们想要做的事情 NSLog(@"调用的自定义的方法: %s",__FUNCTION__); <span style="white-space:pre"> </span> //再调用系统 的默认方法 /*重点 : 为什么 方法名是自定义方法 而不是系统默认的方法名字 是因为load 里面 系统默认方法和自定义的方法进行了交换 系统默认的方法 setImage: 变成了 cz_setImage: 所以 在交换方法完成之后 再次调用系统默认的方法就变成了 我们自定义的方法 */ [self cz_setImage:result]; }
首先先自定义一个设置图片的方法- (void) cz_setImage:(UIImage *)image
里面 输出 当前的方法。
在方法 + (void) load{} 里面添加 我们的交换方法
就3句话,
1、获取系统默认的 设置图片的方法
2、获取自定义的设置图片的方法
3、交换这两个方法
class_getInstanceMethod 获取类的实例的方法
参数:
1、类的实例 [self class] (这是一种特殊的实例化对象)
2、获取的实例方法 SEL
返回值:
Method 方法 类型
method_exchangeImplementations
参数:
1、要交换的第一个方法
2、要交换的第二个方法
重点注意:
在方法交换之后,两个方法已经被交换了,调用系统方法 变成调用自定义的方法名
调用自定义的方法 变成调用系统的方法名
所以下面:
//自定义 的 类 的实例化方法 Cotegory - (void)cz_setImage:(UIImage *)image { //这里是我们想要做的事情 输出当前调用的方法 NSLog(@"调用的自定义的方法: %s",__FUNCTION__); <span> </span> //再调用系统 的默认方法 /*重点 : 为什么 方法名是自定义方法 而不是系统默认的方法名字 是因为load 里面 系统默认方法和自定义的方法进行了交换 系统默认的方法 setImage: 变成了 cz_setImage: 所以 在交换方法完成之后 再次调用系统默认的方法就变成了 我们自定义的方法 */ 这个时候系统默认的方法名 在这里变成了 <span style="font-family: Arial, Helvetica, sans-serif;">cz_setImage:</span> [self cz_setImage:result]; }
ViewController 的运行结果为
#import "UIImageView+RuntimeCrossoverMethod.h"
UIImageView *imageView = [[UIImageView alloc]initWithFrame:CGRectMake(0, 0, 200, 200)]; imageView.center = self.view.center; [self.view addSubview:imageView]; imageView.image = [UIImage imageNamed:@"image5.png"];
2016-10-14 16:38:01.248 Runtime[4947:837294]调用的自定义的方法: -[UIImageView(RuntimeCrossoverMethod) cz_setImage:]
我们调用系统默认的方法 变成调用我们自定义的方法,当然我们在自定义方法里面又掉用了系统的方法(这个时候系统的方法名变成了我们自定义的方法名)。
相关文章推荐
- RunTime的一些用法
- runtime的一些理解与用法
- RunTime的一些用法
- ES6的一些基本用法
- 【关键字】一些关键字用法总结
- c++ map的一些基础函数用法
- List的一些用法
- NSString的一些用法
- CSS伪类的一些用法以及visibility:hidden和display:none的一些区别
- select 一些用法
- java中Math的一些用法
- openpyxl - 一些简单用法
- 关于MFC一些小用法
- 关于正则表达式的一些用法
- Apache Pig的一些基础概念及用法总结2(转)
- 关于self.用法的一些总结
- C语言的一些特殊用法(#define)
- 关于controller的一些用法
- Eclipse 工作集的用法 用法的一些补充
- matplotlib的一些简单用法 英语很重要