ios - Runtime 运行时机制
2017-06-04 19:00
351 查看
一、 Runtime是什么?
1.runtime 叫做运行时机制,其实oc也有运行时机制-消息机制!我们oc中调用方法的时候,其实底层就是发消息。2.c语言中,函数的调用在编译的时候会决定调用哪个函数。
3.oc当中的函数,属于动态调用的过程,在编译的时候并不能决定真正调用哪个函数,只有在运行的时候才会决定调用函数。
那么,现在就可以得出:
(1)在编译阶段,oc可以调用任何函数,即使这个函数病没有实现,只要声明了就不会报错。
(2)在编译阶段,c语言调用未实现的函数就一定会报错!
那么,现在应该清楚运行时机制的特性了吧。
**
二、runtime干什么
**1.发送消息
(1)上面也说了,其实方法调用的本质,就是让对象发消息(消息机制)。
(2)objc_msgSend,只有对象才能发消息,故以objc开头。
(3)使用消息机制,必须导入#import
#import "ViewController.h" @interface ViewController () @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; /* 大家应该都用过这个方法! */ UIImage * image = [UIImage imageNamed:@"123"]; /* 现在呢,我想给imageNamed这个方法添加一个功能需求:加载图片的时候判断图片是否加载成功 第一步:首先想到的是建一个类别category + (instancetype)imageWithName:(NSString *)name; 第二步:交换imageNamed 和imageWithName 调用imageNamed的时候就简洁的调用imageWithName方法! */ }
#import "UIImage+ChangeImage.h" #import <objc/runtime.h> @implementation UIImage (ChangeImage) //这个load 加载分类的时候第一个调用 +(void)load{ //交换方法 //获取imageNamed的方法 Method imageNamed = class_getClassMethod(self, @selector(imageNamed:)); //获取imageWithName的方法 Method imageWithName = class_getClassMethod(self, @selector(imageWithName:)); //交换方法地址,间接的交换方法 method_exchangeImplementations(imageWithName, imageNamed); } //不能在分类里面重写 imageNamed ,因为会把系统的功能给覆盖掉,而且分类中不能调用super。 + (instancetype)imageWithName:(NSString *)name{ //这里调用系统的imageWithName 方法! UIImage * image = [self imageWithName:name]; //这里实现我们的需求:判断图片是否加载成功! if (image == nil) { NSLog(@"图片加载失败"); }else{ NSLog(@"图片加载成功"); } return image; }
3.动态添加方法
(1)应用场景:如果一个类的方法非常多,加载类到内存的时候比较耗费资源,需要给每个方法生成映射表,可以使用动态给某个类,添加方法。
(2)简单的使用:
- (void)viewDidLoad { [super viewDidLoad]; Person * p =[[Person alloc]init]; //现在 person类的对象方法eat方法没有实现,只是声明 [p performSelector:@selector(eat)]; /* 用performSelector调用eat,运行直接会崩溃 那么,动态添加方法就不会报错! */ }
4.给分类添加属性
(1)原理:给一个类声明属性,其实本质就是给类添加关联,并不是直接把这个值的内存空间添加到内存空间。
下面的是我类别里面的代码:
#import "UIView+Personification.h" #import <objc/runtime.h> //定义关联的属性名字 key static const char *key = "sex"; @implementation UIView (Personification) -(NSString *)sex{ //根据上面的key,获取关联的值! return objc_getAssociatedObject(self, key); } - (void)setSex:(NSString * )sex{ /** 第一个参数:给谁添加关联 第二个参数:关联的key扔进去,取的时候用这个key获取 第三个参数:关联的value 就是sex字符串啦 第四个参数:关联的策略 (是一个枚举值) Specifies a strong reference to the associated object. * The association is not made atomically. 简单翻译一下:综上所述一个指定对关联对象的强引用 这个引用不是被原子化的 那不就是->nonatomic */ objc_setAssociatedObject(self, key, sex, OBJC_ASSOCIATION_RETAIN_NONATOMIC); } @end
5.字典转模型
(1)我们写模型属性,需要和字典里的key一一对应
(2)那么问题来了:一个个的生成模型属性,很慢?
(3)解决方案:能否自动的根据一个字典,生成对应的属性呢?
(4)实施方案:提供一个分类,专门根据字典生成对应的属性字符串。
#import "NSObject+LY_Category.h" @implementation NSObject (LY_Category) //自动打印属性字符串 + (void)resloveDictionary:(NSDictionary * )dic{ //拼接属性字符串代码 NSMutableString * strSum = [NSMutableString string]; //1.遍历字典,把字典中的所有的key取出来,生成对应的属性带代码 [dic enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id _Nonnull obj, BOOL * _Nonnull stop) { //类型经常变 抽出来 NSString * type; if ([obj isKindOfClass:NSClassFromString(@"__NSCFString")]) { type = @"NSString"; }else if ([obj isKindOfClass:NSClassFromString(@"__NSCFArray")]){ type = @"NSArray"; }else if ([obj isKindOfClass:NSClassFromString(@"__NSCFNumber")]){ type = @"int"; }else if ([obj isKindOfClass:NSClassFromString(@"__NSDictionary")]){ type =@"NSDictionary"; } //属性字符串 NSString * str; if ([type containsString:@"NS"]) { str = [NSString stringWithFormat:@"@property (nonatomic,strong) %@ * %@",type,key]; }else{ str = [NSString stringWithFormat:@"@property (nonatomic,assign) %@ %@",type,key]; } //每生成属性字符串,自动换行 [strSum appendFormat:@"\n%@\n",str]; }]; //打印拼接好的字符串 NSLog(@"%@",strSum); } @end
(5)字典转模型的方式之一:KVC
#import "LY_DicToMode.h" @implementation LY_DicToMode + (instancetype)LY_DicToModeFromeKVC:(NSDictionary *)dic{ LY_DicToMode * ly = [[self alloc]init]; [ly setValuesForKeysWithDictionary:dic]; return ly; } @end
注意:这种方式必须保证,模型中的属性和字典中的key一一对应!
如果不对应的话,只能重写对象的setValue:forUndefinedKey:,把系统的方法覆盖,
就能继续使用KVC,字典转模型了。
(6)隆重推出第二个字典转模型的方式-Runtime
思路:利用运行时,遍历模型中所有属性,根据模型的属性名,去字典中查找key,取出对应的值,给模型的属性赋值。
步骤:提供一个NSObject分类,专门字典转模型,以后所有模型都可以通过这个分类转。
+ (instancetype)modelWithDic:(NSDictionary *)dic{ //创建对应对象 id objc = [[self alloc]init]; //利用runtime给对象中的成员属性赋值 unsigned int count; // class_copyIvarList:获取类中的所有成员属性 // Ivar:成员属性的意思 // 第一个参数:表示获取哪个类中的成员属性 // 第二个参数:表示这个类有多少成员属性,传入一个Int变量地址,会自动给这个变量赋值 // 返回值Ivar *:指的是一个ivar数组,会把所有成员属性放在一个数组中,通过返回的数组就能全部获取到。 /* 类似下面这种写法 Ivar ivar; Ivar ivar1; Ivar ivar2; // 定义一个ivar的数组a Ivar a[] = {ivar,ivar1,ivar2}; // 用一个Ivar *指针指向数组第一个元素 Ivar *ivarList = a; // 根据指针访问数组第一个元素 ivarList[0]; */ //获取类中所有的成员属性 Ivar * ivarList = class_copyIvarList(self, &count); //遍历属性数组 for (int i = 0; i<count; i++) { //去除成员属性 Ivar ivar = ivarList[i]; //获取成员属性名 NSString * name = [NSString stringWithUTF8String:ivar_getName(ivar)]; // 处理成员属性名->字典中的key // 从第一个角标开始截取 NSString * key = [name substringFromIndex:1]; // 根据成员属性名去字典中查找对应的value id value = dic[key]; // 二级转换:如果字典中还有字典,也需要把对应的字典转换成模型 // 判断下value是否是字典 if ([value isKindOfClass:[NSDictionary class]]) { // 字典转模型 // 获取模型的类对象,调用modelWithDict // 模型的类名已知,就是成员属性的类型 // 获取成员属性类型 NSString * type = [NSString stringWithUTF8String:ivar_getTypeEncoding(ivar)]; // 生成的是这种@"@\"User\"" 类型 -》 @"User" 在OC字符串中 \" -> ",\是转义的意思,不占用字符 // 裁剪类型字符串 NSRange range = [type rangeOfString:@"\""]; type = [type substringFromIndex:range.location + range.length]; range = [type rangeOfString:@"\""]; type = [type substringFromIndex:range.location]; //根据字符串类名生成类对象 Class modelClass = NSClassFromString(type); if (modelClass) { //有对应的模型才需要转 //把字典转成模型 value = [modelClass modelWithDic:value]; } } // 三级转换:NSArray中也是字典,把数组中的字典转换成模型. // 判断值是否是数组 if ([value isKindOfClass:[NSArray class]]) { // 判断对应类有没有实现字典数组转模型数组的协议 if ([self respondsToSelector:@selector(arrayContainModelClass)]) { // 转换成id类型,就能调用任何对象的方法 id idSelf = self; // 获取数组中字典对应的模型 NSString *type = [idSelf arrayContainModelClass][key]; // 生成模型 Class classModel = NSClassFromString(type); NSMutableArray *arrM = [NSMutableArray array]; // 遍历字典数组,生成模型数组 for (NSDictionary *dict in value) { // 字典转模型 id model = [classModel modelWithDict:dict]; [arrM addObject:model]; } // 把模型数组赋值给value value = arrM; } } if (value) { // 有值,才需要给模型的属性赋值 // 利用KVC给模型中的属性赋值 [objc setValue:value forKey:key]; } } return objc; } @end
相关文章推荐
- iOS-浅谈runtime运行时机制02-runtime简单使用
- iOS-浅谈runtime运行时机制02-runtime简单使用
- iOS-浅谈runtime运行时机制01-类与对象的内部结构
- iOS-浅谈runtime运行时机制02-runtime简单使用
- iOS-浅谈runtime运行时机制01-类与对象的内部结构
- IOS 运行时(runtime)机制
- iOS runtime运行时机制
- iOS-浅谈runtime运行时机制02-runtime简单使用
- iOS runtime 运行时机制
- 浅谈iOS运行时机制runtime(1)
- iOS中runtime运行机制解析
- iOS runtime运行时机制
- IOSruntime : 运行时机制
- iOS开发之runtime运行时机制
- [IOS 开发] runtime 运行时机制 完全解读
- iOS开发——高级技术OC篇&运行时(Runtime)机制
- iOS-浅谈runtime运行时机制02-runtime简单使用
- iOS-浅谈runtime运行时机制02-runtime简单使用
- iOS 运行时机制(runtime)
- iOS-浅谈runtime运行时机制02