一: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 = [modelClass modelWithDict:value]; 6:一般某个方法接受传递进来的参数的时候,要判断参数是否为空,为nil或是为空值,给某个值赋值的时候,也要判断该值是否存在:
// 给模型中属性赋值
if (value) {
[objc setValue:value forKey:key];
} */ @implementation NSObject (Model) // Ivar:成员变量 以下划线开头
// Property:属性
+ (instancetype)modelWithDict:(NSDictionary *)dict
{
id objc = [[self alloc] init]; // runtime:根据模型中属性,去字典中取出对应的value给模型属性赋值
// 1.获取模型中所有成员变量 key
// 获取哪个类的成员变量
// count:成员变量个数
unsigned int count = ;
// 获取成员变量数组
Ivar *ivarList = class_copyIvarList(self, &count); // 遍历所有成员变量
for (int i = ; 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 = dict[key]; // 二级转换:判断下value是否是字典,如果是,字典转换层对应的模型
// 并且是自定义对象才需要转换
if ([value isKindOfClass:[NSDictionary class]] && ![ivarType hasPrefix:@"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 = ;
} @end

ios开发runtime学习五:KVC以及KVO,利用runtime实现字典转模型的更多相关文章

  1. iOS开发——UI基础-懒加载,plist文件,字典转模型,自定义view

    一.懒加载 只有使用到了商品数组才会创建数组 保证数组只会被创建一次 只要能够保证数组在使用时才创建, 并且只会创建一次, 那么我们就称之为懒加载 lazy - (void)viewDidLoad 控 ...

  2. iOS开发系列--Objective-C之KVC、KVO

    概述 由于ObjC主要基于Smalltalk进行设计,因此它有很多类似于Ruby.Python的动态特性,例如动态类型.动态加载.动态绑定等.今天我们着重介绍ObjC中的键值编码(KVC).键值监听( ...

  3. iOS开发系列--Objective-C 之 KVC、KVO

    概述 由于ObjC主要基于Smalltalk进行设计,因此它有很多类似于Ruby.Python的动态特性,例如动态类型.动态加载.动态绑定等.今天我们着重介绍ObjC中的键值编码(KVC).键值监听( ...

  4. iOS 开发笔记-Objective-C之KVC、KVO

    概述 键值编码(KVC).键值监听(KVO)特性 键值监听KVO Key Value Observing(简称KVO)其实是一种观察者模式,利用它可以很容易实现视图组件和数据模型的分离,当数据模型的属 ...

  5. ios开发网络学习五:MiMEType ,多线程下载文件思路,文件的压缩和解压缩

    一:MiMEType:一般可以再百度上搜索到相应文件的MiMEType,或是利用c语言的api去获取文件的MiMEType : //对该文件发送一个异步请求,拿到文件的MIMEType - (void ...

  6. ios开发网络学习五:输出流以及文件上传

    一:输出流 #import "ViewController.h" @interface ViewController ()<NSURLConnectionDataDelega ...

  7. iOS开发如何学习前端(2)

    iOS开发如何学习前端(2) 上一篇成果如下. 实现的效果如下. 实现了一个横放的<ul>,也既iOS中的UITableView. 实现了当鼠标移动到列表中的某一个<li>,也 ...

  8. iOS开发如何学习前端(1)

    iOS开发如何学习前端(1) 我为何学前端?因为无聊. 概念 前端大概三大块. HTML CSS JavaScript 基本上每个概念在iOS中都有对应的.HTML请想象成只能拉Autolayout或 ...

  9. 关于iOS开发的学习

    关于iOS开发的学习,打个比方就像把汽车分解:    最底层的原料有塑料,钢铁    再用这些底层的东西造出来发动机,座椅    最后再加上写螺丝,胶水等,把汽车就拼起来了 iOS基本都是英文的资料, ...

随机推荐

  1. vim-录制命令的使用

    使用vim时无意间触碰到q键,左下角出现“recording”这个标识,这是vim的一个强大功能. 他可以录 制一个宏(Macro),在开始记录后,会记录你所有的键盘输入,包括在insert模式下的输 ...

  2. 1.23 Python知识进阶 - 面向对象编程

    一.编程方法 1.函数式编程:"函数式编程"是一种"编程范式"(programming paradigm),也就是如何编写程序的方法论.它属于"结构化 ...

  3. Linux下基于LDAP统一用户认证的研究

    Linux下基于LDAP统一用户认证的研究                   本文出自 "李晨光原创技术博客" 博客,谢绝转载!

  4. BZOJ2160: 拉拉队排练(Manacher)

    Description 艾利斯顿商学院篮球队要参加一年一度的市篮球比赛了.拉拉队是篮球比赛的一个看点,好的拉拉队往往能帮助球队增加士气,赢得最终的比赛.所以作为拉拉队队长的楚雨荨同学知道,帮助篮球队训 ...

  5. CISP/CISA 每日一题 16

    CISA 每日一题(答) 作业调度软件的优点: 1.作业信息仅需建立一次,减少错误发生概率: 2.可定义作业间的依赖关系,当某一项作业失败时,依赖于该作业的后续作业就不会被执行: 3.所有成功或失败的 ...

  6. 【习题 7-2 UVA-225】Golygons

    [链接] 我是链接,点我呀:) [题意] 在这里输入题意 [题解] 暴力枚举每次走哪里就好. 用一个二维数组来判重.(数据里,要求不能经过一个点两次->但路径可以相交 然后再用一个flag数组, ...

  7. mysql中load data Infile运用

    速度比insert要快20倍.共享一下java程序操作. package com.mysql.csv; import java.sql.Connection; import java.sql.Driv ...

  8. Font Awesome 图标如何使用

    Font Awesome 图标如何使用 一.总结 一句话总结:Font Awesome字体图标非常好用,直接引入font-awesome.css,然后就可以直接使用了,使用的时候是用的i标签. 1.字 ...

  9. delete noprompt archivelog 报错ORA-00245,RMAN-08132

    在RMAN执行  delete noprompt archivelog until time 'sysdate-1';  报错   ORA-00245: control file backup fai ...

  10. 学习笔记:Vue——混入

    前言: 到现在用Vue做了不少项目了,用到的都是初阶的功能,很多高阶能力都没有用到.仅用初级阶段也能做项目,甚至是复杂项目,可见vue之强大,果然是渐进式开发方式. 但是本着虚心学习的态度,还是要抽空 ...