首页 > 代码库 > ios开发runtime学习五:KVC以及KVO,利用runtime实现字典转模型
ios开发runtime学习五:KVC以及KVO,利用runtime实现字典转模型
一:KVC和KVO的学习
#import "StatusItem.h"/* 1:总结:KVC赋值:1:setValuesForKeysWithDictionary实现原理:遍历字典,得到所有的key,value值,再利用kvc, setVaue forkey来为value赋值 2: [item setValue:@"来自即刻笔记" forKey:@"source"],内部的底层实现, 1.首先去模型中查找有没有setSource,找到,直接调用赋值 [self setSource:@"来自即刻笔记"] 2.去模型中查找有没有source属性,有,直接访问属性赋值 source = value 3.去模型中查找有没有_source属性,有,直接访问属性赋值 _source = value 4.找不到,就会直接报错 setValue:forUndefinedKey:报找不到的错误 当系统找不到就会调用这个方法,报错- (void)setValue:(id)value forUndefinedKey:(NSString *)key可以重写此方法更改key值 3:KVC四个方法:利用kvc可以访问成员变量和属性,setValue,value为属性值,forKey,key为属性名,forKeyPath为键值路径,例如在model中有如下属性定义: @property (nonatomic, strong) BankAccount *account; keyPath: [zhangSan setValue:@150 forKeyPath:@"account.balance"]; - (id)valueForKey:(NSString *)key; - (id)valueForKeyPath:(NSString *)keyPath; - (void)setValue:(id)value forKey:(NSString *)key; - (void)setValue:(id)value forKeyPath:(NSString *)keyPath; 4:KVO:键值观察机制,用于对属性的value值的改变做监听:用法: @interface BankAccount : NSObject @property (nonatomic, assign) NSInteger balance; @end @interface Person : NSObject @property (nonatomic, strong) BankAccount *account; @end @implementation Person - (instancetype)init { ... // 注册Observer: [self.account addObserver:self forKeyPath:@"balance" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:nil]; ... } - (void)dealloc { // 不要忘了removeObserver [self.account removeObserver:self forKeyPath:@"balance"]; } // 属性变化的回调方法: - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { if ([keyPath isEqualToString:@"balance"]) { NSLog(@"Balance was %@.", change[NSKeyValueChangeOldKey]); NSLog(@"Balance is %@ now.", change[NSKeyValueChangeNewKey]); } } @end - (void)testKVO { Person *zhangSan = [[Person alloc] initWithName:@"ZhangSan" andBalance:20]; // 无论是用点语法还是KVC的方法都会触发回调: zhangSan.account.balance = 150; [zhangSan setValue:@250 forKeyPath:@"account.balance"]; } */@interface StatusItem ()@property (nonatomic,copy)NSString *hello;@end@implementation StatusItem// 模型只保存最重要的数据,导致模型的属性和字典不能一一对应+ (instancetype)itemWithDict:(NSDictionary *)dict{ StatusItem *item = [[self alloc] init]; // KVC:把字典中所有值给模型的属性赋值 [item setValuesForKeysWithDictionary:dict]; // 拿到每一个模型属性,去字典中取出对应的值,给模型赋值 // 从字典中取值,不一定要全部取出来 // MJExtension:字典转模型 runtime:可以把一个模型中所有属性遍历出来 // MJExtension:封装了很多层// item.pic_urls = dict[@"pic_urls"];// item.created_at = dict[@"created_at"]; // KVC原理: // 1.遍历字典中所有key,去模型中查找有没有对应的属性 [dict enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id _Nonnull value, BOOL * _Nonnull stop) { // 2.去模型中查找有没有对应属性 KVC // key:source value:来自即刻笔记 // [item setValue:@"来自即刻笔记" forKey:@"source"] [item setValue:value forKey:key]; }]; return item;}// 重写系统方法? 1.想给系统方法添加额外功能 2.不想要系统方法实现// 系统找不到就会调用这个方法,报错- (void)setValue:(id)value forUndefinedKey:(NSString *)key{ }@end
二:利用runtime实现字典转模型
#import "ViewController.h"#import "NSDictionary+Property.h"#import "StatusItem.h"#import "NSObject+Model.h"@interface ViewController ()@end/* 总结:1:项目中的文件都保存在mainBundle里,读取项目中的本地信息:[[NSBundle mainBundle] pathForResource:@"status.plist" ofType:nil]; 得到本地路径path,再看项目中的文件根节点是字典还是数组,再从中读取本地路径filer:dictionaryWithContentsOfFile读取 。若是获取网络端的路径:dictionaryWithContentsOfUrl */@implementation ViewController- (void)viewDidLoad { [super viewDidLoad]; // 获取文件全路径 NSString *filePath = [[NSBundle mainBundle] pathForResource:@"status.plist" ofType:nil]; // 文件全路径 NSDictionary *dict = [NSDictionary dictionaryWithContentsOfFile:filePath]; // 设计模型,创建属性代码 => dict// [dict[@"user"] createPropertyCode]; // 字典转模型:KVC,MJExtension StatusItem *item = [StatusItem modelWithDict:dict]; }@end
#import <Foundation/Foundation.h>// 字典转模型@interface NSObject (Model)+ (instancetype)modelWithDict:(NSDictionary *)dict;@end
#import "NSObject+Model.h"#import <objc/message.h>/* 总结:MJExtension字典转模型的底层核心实现:runtime实现字典转模型。 1:因为模型model都继承NSObject,所以可以给系统类写分类进行拓展,子类继承NSObject,也就是继承了扩展的方法。所以模型转字典的方法考虑给NSObject写一个分类进行方法的拓展 2:在分类中若是对象方法,self指的是调用该方法的对象,类方法中self指的是调用该方法的类。方法的设计:类方法简单粗暴,直接用类去调用,字典转模型方法获得所转换的模型,不用创建对象,并且会将调用方法的类作为参数传进方法中(对象方法也如此) 3:原理:runtime:根据模型中属性,去字典中取出对应的value给模型属性赋值 1:先获取模型中所有成员变量 key 参数意义: // 获取哪个类的成员变量 // count:成员变量个数 int *类型 unsigned int count = 0; // 获取成员变量数组 Ivar *ivarList = class_copyIvarList(self, &count); 注意:1:int *count ,此count的类型为int *类型,当作为参数的时候,需要传入一个int *类型的指针,指针里存放的都是内存地址,也就是将地址作为参数传递,当方法执行完毕后,系统会拿到*count 进行赋值 int a = 2; int b = 3; int c = 4; int arr[] = {a,b,c}; int *p = arr; p[0]; NSLog(@"%d %d",p[0],p[1]); 2: 获取类里面属性 class_copyPropertyList(<#__unsafe_unretained Class cls#>, <#unsigned int *outCount#>) 获取类里面所有方法 class_copyMethodList(<#__unsafe_unretained Class cls#>, <#unsigned int *outCount#>)// 本质:创建谁的对象 3:获取模型中所有成员变量 key,Ivar:成员变量 以下划线开头,相当于一个数组 // 获取哪个类的成员变量 // count:成员变量个数 unsigned int count = 0; // 获取成员变量数组 Ivar *ivarList = class_copyIvarList(self, &count); 4:具体实现:获取成员变量名字:ivar_getName(ivar),属于c语言字符串,所以要进行UTF8编码 获取成员变量类型:ivar_getTypeEncoding(ivar) // 获取成员变量名字 NSString *ivarName = [NSString stringWithUTF8String:ivar_getName(ivar)]; // 获取成员变量类型 NSString *ivarType = [NSString stringWithUTF8String:ivar_getTypeEncoding(ivar)]; 注意:1:字符串替换:stringByReplacingOccurrencesOfString 2:字符串截取:substringFromIndex:包括该index substringToIndex:不包括该index 3:前后缀:hasPrefix前缀,hasSuffix:后缀 4:是否包含某个字符串:containString 5:字符串转换为Class:NSClassFromString: // 获取类 Class modelClass = NSClassFromString(ivarType); value = http://www.mamicode.com/[modelClass modelWithDict:value];>*/@implementation NSObject (Model)// Ivar:成员变量 以下划线开头// Property:属性+ (instancetype)modelWithDict:(NSDictionary *)dict{ id objc = [[self alloc] init]; // runtime:根据模型中属性,去字典中取出对应的value给模型属性赋值 // 1.获取模型中所有成员变量 key // 获取哪个类的成员变量 // count:成员变量个数 unsigned int count = 0; // 获取成员变量数组 Ivar *ivarList = class_copyIvarList(self, &count); // 遍历所有成员变量 for (int i = 0; i < count; i++) { // 获取成员变量 Ivar ivar = ivarList[i]; // 获取成员变量名字 NSString *ivarName = [NSString stringWithUTF8String:ivar_getName(ivar)]; // 获取成员变量类型 NSString *ivarType = [NSString stringWithUTF8String:ivar_getTypeEncoding(ivar)]; // @\"User\" -> User ivarType = [ivarType stringByReplacingOccurrencesOfString:@"\"" withString:@""]; ivarType = [ivarType stringByReplacingOccurrencesOfString:@"@" withString:@""]; // 获取key NSString *key = [ivarName substringFromIndex:1]; // 去字典中查找对应value // key:user value:NSDictionary id value = http://www.mamicode.com/dict[key];>"NS"]) { // 字典转换成模型 userDict => User模型 // 转换成哪个模型 // 获取类 Class modelClass = NSClassFromString(ivarType); value = [modelClass modelWithDict:value]; } // 给模型中属性赋值 if (value) { [objc setValue:value forKey:key]; } } return objc;}void test(int *count){ *count = 3;}@end
ios开发runtime学习五:KVC以及KVO,利用runtime实现字典转模型
声明:以上内容来自用户投稿及互联网公开渠道收集整理发布,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任,若内容有误或涉及侵权可进行投诉: 投诉/举报 工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。