一:KVC和KVO的学习

  1. #import "StatusItem.h"
  2. /*
  3. 1:总结:KVC赋值:1:setValuesForKeysWithDictionary实现原理:遍历字典,得到所有的key,value值,再利用kvc, setVaue forkey来为value赋值 2: [item setValue:@"来自即刻笔记" forKey:@"source"],内部的底层实现,
  4. 1.首先去模型中查找有没有setSource,找到,直接调用赋值 [self setSource:@"来自即刻笔记"]
  5. 2.去模型中查找有没有source属性,有,直接访问属性赋值 source = value
  6. 3.去模型中查找有没有_source属性,有,直接访问属性赋值 _source = value
  7. 4.找不到,就会直接报错 setValue:forUndefinedKey:报找不到的错误
  8. 当系统找不到就会调用这个方法,报错- (void)setValue:(id)value forUndefinedKey:(NSString *)key可以重写此方法更改key值
  9. 3:KVC四个方法:利用kvc可以访问成员变量和属性,setValue,value为属性值,forKey,key为属性名,forKeyPath为键值路径,例如在model中有如下属性定义:
  10. @property (nonatomic, strong) BankAccount *account;
  11. keyPath:
  12. [zhangSan setValue:@150 forKeyPath:@"account.balance"];
  13.  
  14. - (id)valueForKey:(NSString *)key;
  15. - (id)valueForKeyPath:(NSString *)keyPath;
  16. - (void)setValue:(id)value forKey:(NSString *)key;
  17. - (void)setValue:(id)value forKeyPath:(NSString *)keyPath;
  18.  
  19. 4:KVO:键值观察机制,用于对属性的value值的改变做监听:用法:
  20. @interface BankAccount : NSObject
  21. @property (nonatomic, assign) NSInteger balance;
  22. @end
  23.  
  24. @interface Person : NSObject
  25. @property (nonatomic, strong) BankAccount *account;
  26. @end
  27.  
  28. @implementation Person
  29. - (instancetype)init {
  30. ...
  31. // 注册Observer:
  32. [self.account addObserver:self
  33. forKeyPath:@"balance"
  34. options:NSKeyValueObservingOptionNew |
  35. NSKeyValueObservingOptionOld
  36. context:nil];
  37. ...
  38. }
  39.  
  40. - (void)dealloc {
  41. // 不要忘了removeObserver
  42. [self.account removeObserver:self forKeyPath:@"balance"];
  43. }
  44.  
  45. // 属性变化的回调方法:
  46. - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
  47. if ([keyPath isEqualToString:@"balance"]) {
  48. NSLog(@"Balance was %@.", change[NSKeyValueChangeOldKey]);
  49. NSLog(@"Balance is %@ now.", change[NSKeyValueChangeNewKey]);
  50. }
  51. }
  52. @end
  53.  
  54. - (void)testKVO {
  55. Person *zhangSan = [[Person alloc] initWithName:@"ZhangSan" andBalance:20];
  56. // 无论是用点语法还是KVC的方法都会触发回调:
  57. zhangSan.account.balance = 150;
  58. [zhangSan setValue:@250 forKeyPath:@"account.balance"];
  59. }
  60.  
  61. */
  62. @interface StatusItem ()
  63. @property (nonatomic,copy)NSString *hello;
  64. @end
  65. @implementation StatusItem
  66.  
  67. // 模型只保存最重要的数据,导致模型的属性和字典不能一一对应
  68.  
  69. + (instancetype)itemWithDict:(NSDictionary *)dict
  70. {
  71. StatusItem *item = [[self alloc] init];
  72.  
  73. // KVC:把字典中所有值给模型的属性赋值
  74. [item setValuesForKeysWithDictionary:dict];
  75.  
  76. // 拿到每一个模型属性,去字典中取出对应的值,给模型赋值
  77. // 从字典中取值,不一定要全部取出来
  78. // MJExtension:字典转模型 runtime:可以把一个模型中所有属性遍历出来
  79. // MJExtension:封装了很多层
  80. // item.pic_urls = dict[@"pic_urls"];
  81. // item.created_at = dict[@"created_at"];
  82.  
  83. // KVC原理:
  84. // 1.遍历字典中所有key,去模型中查找有没有对应的属性
  85. [dict enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id _Nonnull value, BOOL * _Nonnull stop) {
  86.  
  87. // 2.去模型中查找有没有对应属性 KVC
  88. // key:source value:来自即刻笔记
  89. // [item setValue:@"来自即刻笔记" forKey:@"source"]
  90. [item setValue:value forKey:key];
  91.  
  92. }];
  93.  
  94. return item;
  95. }
  96.  
  97. // 重写系统方法? 1.想给系统方法添加额外功能 2.不想要系统方法实现
  98. // 系统找不到就会调用这个方法,报错
  99. - (void)setValue:(id)value forUndefinedKey:(NSString *)key
  100. {
  101.  
  102. }
  103.  
  104. @end

二:利用runtime实现字典转模型

  1. #import "ViewController.h"
  2. #import "NSDictionary+Property.h"
  3. #import "StatusItem.h"
  4. #import "NSObject+Model.h"
  5. @interface ViewController ()
  6.  
  7. @end
  8. /*
  9. 总结:1:项目中的文件都保存在mainBundle里,读取项目中的本地信息:[[NSBundle mainBundle] pathForResource:@"status.plist" ofType:nil]; 得到本地路径path,再看项目中的文件根节点是字典还是数组,再从中读取本地路径filer:dictionaryWithContentsOfFile读取 。若是获取网络端的路径:dictionaryWithContentsOfUrl
  10. */
  11. @implementation ViewController
  12.  
  13. - (void)viewDidLoad {
  14. [super viewDidLoad];
  15.  
  16. // 获取文件全路径
  17. NSString *filePath = [[NSBundle mainBundle] pathForResource:@"status.plist" ofType:nil];
  18.  
  19. // 文件全路径
  20. NSDictionary *dict = [NSDictionary dictionaryWithContentsOfFile:filePath];
  21.  
  22. // 设计模型,创建属性代码 => dict
  23. // [dict[@"user"] createPropertyCode];
  24.  
  25. // 字典转模型:KVC,MJExtension
  26. StatusItem *item = [StatusItem modelWithDict:dict];
  27.  
  28. }
  29.  
  30. @end
  1. #import <Foundation/Foundation.h>
  2. // 字典转模型
  3. @interface NSObject (Model)
  4.  
  5. + (instancetype)modelWithDict:(NSDictionary *)dict;
  6.  
  7. @end
  1. #import "NSObject+Model.h"
  2. #import <objc/message.h>
  3. /*
  4.  
  5. 总结:MJExtension字典转模型的底层核心实现:runtime实现字典转模型。
  6.  
  7. 1:因为模型model都继承NSObject,所以可以给系统类写分类进行拓展,子类继承NSObject,也就是继承了扩展的方法。所以模型转字典的方法考虑给NSObject写一个分类进行方法的拓展
  8. 2:在分类中若是对象方法,self指的是调用该方法的对象,类方法中self指的是调用该方法的类。方法的设计:类方法简单粗暴,直接用类去调用,字典转模型方法获得所转换的模型,不用创建对象,并且会将调用方法的类作为参数传进方法中(对象方法也如此)
  9.  
  10. 3:原理:runtime:根据模型中属性,去字典中取出对应的value给模型属性赋值
  11. 1:先获取模型中所有成员变量 key
  12. 参数意义:
  13. // 获取哪个类的成员变量
  14. // count:成员变量个数 int *类型
  15. unsigned int count = 0;
  16. // 获取成员变量数组
  17. Ivar *ivarList = class_copyIvarList(self, &count);
  18.  
  19. 注意:1:int *count ,此count的类型为int *类型,当作为参数的时候,需要传入一个int *类型的指针,指针里存放的都是内存地址,也就是将地址作为参数传递,当方法执行完毕后,系统会拿到*count 进行赋值
  20. int a = 2;
  21. int b = 3;
  22. int c = 4;
  23. int arr[] = {a,b,c};
  24. int *p = arr;
  25. p[0];
  26. NSLog(@"%d %d",p[0],p[1]);
  27.  
  28. 2: 获取类里面属性
  29. class_copyPropertyList(<#__unsafe_unretained Class cls#>, <#unsigned int *outCount#>)
  30.  
  31. 获取类里面所有方法
  32. class_copyMethodList(<#__unsafe_unretained Class cls#>, <#unsigned int *outCount#>)// 本质:创建谁的对象
  33.  
  34. 3:获取模型中所有成员变量 key,Ivar:成员变量 以下划线开头,相当于一个数组
  35. // 获取哪个类的成员变量
  36. // count:成员变量个数
  37. unsigned int count = 0;
  38. // 获取成员变量数组
  39. Ivar *ivarList = class_copyIvarList(self, &count);
  40.  
  41. 4:具体实现:获取成员变量名字:ivar_getName(ivar),属于c语言字符串,所以要进行UTF8编码 获取成员变量类型:ivar_getTypeEncoding(ivar)
  42. // 获取成员变量名字
  43. NSString *ivarName = [NSString stringWithUTF8String:ivar_getName(ivar)];
  44. // 获取成员变量类型
  45. NSString *ivarType = [NSString stringWithUTF8String:ivar_getTypeEncoding(ivar)];
  46.  
  47. 注意:1:字符串替换:stringByReplacingOccurrencesOfString 2:字符串截取:substringFromIndex:包括该index substringToIndex:不包括该index 3:前后缀:hasPrefix前缀,hasSuffix:后缀 4:是否包含某个字符串:containString 5:字符串转换为Class:NSClassFromString: // 获取类
  48. Class modelClass = NSClassFromString(ivarType);
  49.  
  50. value = [modelClass modelWithDict:value];
  51.  
  52. 6:一般某个方法接受传递进来的参数的时候,要判断参数是否为空,为nil或是为空值,给某个值赋值的时候,也要判断该值是否存在:
  53. // 给模型中属性赋值
  54. if (value) {
  55. [objc setValue:value forKey:key];
  56. }
  57.  
  58. */
  59.  
  60. @implementation NSObject (Model)
  61.  
  62. // Ivar:成员变量 以下划线开头
  63. // Property:属性
  64. + (instancetype)modelWithDict:(NSDictionary *)dict
  65. {
  66. id objc = [[self alloc] init];
  67.  
  68. // runtime:根据模型中属性,去字典中取出对应的value给模型属性赋值
  69. // 1.获取模型中所有成员变量 key
  70. // 获取哪个类的成员变量
  71. // count:成员变量个数
  72. unsigned int count = ;
  73. // 获取成员变量数组
  74. Ivar *ivarList = class_copyIvarList(self, &count);
  75.  
  76. // 遍历所有成员变量
  77. for (int i = ; i < count; i++) {
  78. // 获取成员变量
  79. Ivar ivar = ivarList[i];
  80.  
  81. // 获取成员变量名字
  82. NSString *ivarName = [NSString stringWithUTF8String:ivar_getName(ivar)];
  83. // 获取成员变量类型
  84. NSString *ivarType = [NSString stringWithUTF8String:ivar_getTypeEncoding(ivar)];
  85. // @\"User\" -> User
  86. ivarType = [ivarType stringByReplacingOccurrencesOfString:@"\"" withString:@""];
  87. ivarType = [ivarType stringByReplacingOccurrencesOfString:@"@" withString:@""];
  88. // 获取key
  89. NSString *key = [ivarName substringFromIndex:1];
  90.  
  91. // 去字典中查找对应value
  92. // key:user value:NSDictionary
  93.  
  94. id value = dict[key];
  95.  
  96. // 二级转换:判断下value是否是字典,如果是,字典转换层对应的模型
  97. // 并且是自定义对象才需要转换
  98. if ([value isKindOfClass:[NSDictionary class]] && ![ivarType hasPrefix:@"NS"]) {
  99. // 字典转换成模型 userDict => User模型
  100. // 转换成哪个模型
  101.  
  102. // 获取类
  103. Class modelClass = NSClassFromString(ivarType);
  104.  
  105. value = [modelClass modelWithDict:value];
  106. }
  107.  
  108. // 给模型中属性赋值
  109. if (value) {
  110. [objc setValue:value forKey:key];
  111. }
  112. }
  113.  
  114. return objc;
  115. }
  116.  
  117. void test(int *count){
  118. *count = ;
  119. }
  120.  
  121. @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. Git管理软件

    软件下载地址 首先下载库 https://code.google.com/p/msysgit/ 接着安装 https://code.google.com/p/tortoisegit/ 然后就能够用了

  2. ViewPager 入门一

    使用ViewPager能够得到不同view的切换效果 例如以下图,实现了四个view间的相互滑动 一.新建项目,引入ViewPager控件 ViewPager.它是google SDk中自带的一个附加 ...

  3. worktools-git 工具的使用总结(知识点累积)

    1.用简单列表的方式查看提交记录git log --pretty=online zhangshuli@zhangshuli-MS-:~/myGit$ git log --pretty=oneline ...

  4. holder.js如何使用

    holder.js的使用 一.总结 一句话总结:使用:holder.js后面接图片宽高 <img src="holder.js/300x200" /> 1.holder ...

  5. golang 函数传值

    package main import ( "fmt" ) type Vertex struct { X, Y int } func dop(a Vertex, vl int) { ...

  6. react radio onchange事件点击无效

    记: 项目需求:   页面中radio默认选中        第一次进去页面   点击radio的时候不管怎样点击    都是选中 连onChange事件都没触发 进入页面  点击刷新   点击rad ...

  7. BZOJ3676: [Apio2014]回文串(SAM+Manacher/PAM)

    Description 考虑一个只包含小写拉丁字母的字符串s.我们定义s的一个子串t的“出 现值”为t在s中的出现次数乘以t的长度.请你求出s的所有回文子串中的最 大出现值. Input 输入只有一行 ...

  8. get_browser()用法

    get_browser()用法 get_browser()函数是用来分析USER_AGENT的,它的执行方法是自动获取客户端的USER_AGENT,然后调用browscap.ini库进行分析得到结果 ...

  9. 转换PHP脚本成为windows的执行程序

    转换PHP脚本成为windows的执行程序 Convert a PHP script into a stand-alone windows executable I want to automate ...

  10. IIS7性能优化

    http://www.03389.com/BLOG/user1/qq66565841/archives/2014/2014112143553.html IIS7性能优化 IIS7 优化-网站请求并发数 ...