当前位置: 首页 > news >正文

ios开发之你真的了解了KVC吗?

之前一直以为自己了解KVC 其实只是井底之蛙啦,真正的要把KVC原原本本的说出来还真的不是一件简单的事情。于是今天写下这边文章,告诫自己要知其然还要知其所以然。

Key-value coding,它是一种使用字符串标识符,间接访问对象属性的机制,而不是直接调用getter 和 setter方法。通常我们使用valueForKey 来替代getter 方法,setValue:forKey来代替setter方法。

下面是使用KVC 和 不使用 KVC的代码对比

Persion *persion =  [ [Persion alloc] init ];

//不使用KVC
persion.name = @"hufeng" ;

//使用KVC的写法
[persion  setValue:@"hufeng" forKey:@"name"];

看出区别来了吗?你可能会说 你写的太简单了,我们实际用的时候不可能有这样复杂的类,下面我们写个复杂点的:我们有一个人 这个人有一个手机类 这个手机类 有一个电池类 我们要获取这个电池类 比之前复杂了吧。

没有KVC

Persion *persion =  [ [Persion alloc] init ];

Phone *phone = persion.phone;

Battery *battery = phone.battery;

使用KVC

Battery *battery = [persion valueForKeyPath: @"phone.battery" ];

注意- valueForKeyPath 里面的值是区分大小写的,你如果写出Phone.Battery 是不行的

说到这里你可能会问 我能不能对 NSArray 调用KVC吗? 答案是否定的,因为array 没有keys啊,但是你可以对array里面的item 使用KVC。

KVC 最常用的还是在序列化和反序列话对象。我们经常需要把json字符串反序列化成我们想要的对象 下面是一个例子 将字典用NSKeyedArchiver 序列化成对象

- (id)initWithDictionary:(NSDictionary *)dictionary {

    self = [self init];

    if (self){

        [self setValuesForKeysWithDictionary:dictionary];

    }

    return self;

}

注意 这里有一个坑 当我们setValue 给一个没有定义的字典值(forUndefinedKey)时 会抛出NSUndefinedKeyException异常的 记的处理此种情况

还有一个需要注意的是KVC 并没有类型检验,毕竟Object-C 还是动态的啦。 还是看下面的代码吧

[persion setValue:[NSNumber numberWithInteger:1] forKey:@"name"]; 
// compiles and runs

persion.name = [NSNumber numberWithInteger:1]; 
// won't compile: Incompatible pointer types assigning to 'NSString *' from 'NSNumber *'

setValue forKey 得到的对象是泛型的id, 只有在使用的时候才能确定类型。你可能会问不至于吧 OC 这样弱啊,当然不是 OC 提供了了一个方法validateValue来解决这个问题

@property (nonatomic, strong) NSString name;

- (BOOL)validateName:(id*)ioValue error:(NSError**)error {
    // Validation logic goes here

}

Person *p = [Person new];

NSString *name = @"Jason Hu";

NSError *error = nil;


// This call below actually calls our validateName: error: method

if ([p validateValue:&name forKey:@"name" error:&error]) {

    [p setValue:name forKey:@"name"];
}

你可能会问写这样多代码 才只验证了一个属性 那如果我这个类有n+个属性 难道我要写n+个验证方法吗?


到这里我们对KVC已经有了一个初步印象,到这里其实还只是冰山一角。
下面我们要提高更高的要求,如果让key 支持 不区分大小写

下面我们提到一个方法initialize

initialize是在类或者其子类的第一个方法被调用前调用。所以如果类没有被引用进项目或者类文件被引用进来,但是没有使用,那么initialize也不会被调用 ,到这里 知道我们接下来要干嘛了吧

+ (void)initialize {
    [super initialize];

    dispatch_once(&onceToken, ^{
        modelProperties = [NSMutableDictionary dictionary];
        propertyTypesArray = @[/* removed for brevity */];
    });
    NSMutableDictionary *translateNameDict = [NSMutableDictionary dictionary];
    [self hydrateModelProperties:[self class] translateDictionary:translateNameDict];
    [modelProperties setObject:translateNameDict forKey:[self calculateClassName]];
}

+ (void)hydrateModelProperties:(Class)class translateDictionary:(NSMutableDictionary *)translateDictionary {
    if (!class || class == [NSObject class]){
        return;
    }

    unsigned int outCount, i;
    objc_property_t *properties = class_copyPropertyList(class, &outCount);
    for (i = 0; i < outCount; i++){
        objc_property_t p = properties[i];
        const char *name = property_getName(p);
        NSString *nsName = [[NSString alloc] initWithCString:name encoding:NSUTF8StringEncoding];
        NSString *lowerCaseName = [nsName lowercaseString];
        [translateDictionary setObject:nsName forKey:lowerCaseName];
        //注意此处哦
        NSString *propertyType = [self getPropertyType:p];
        [self addValidatorForProperty:nsName type:propertyType];
    }
    free(properties);

    [self hydrateModelProperties:class_getSuperclass(class) translateDictionary:translateDictionary];
}

相关文章:

  • 快速排序算法
  • C# 网络编程之webBrowser乱码问题及解决知识
  • Python 入门教程 8 ---- Python Lists and Dictionaries
  • linux上安装RAC时不使用asmlib的多路径配置
  • HDOJ, 杭电1219, ACme简单字符串题
  • Java RandomAccessFile
  • Sass的准备工作有哪些
  • oracle RAC 10g 升级到11g (out of place) 回退方案
  • 个人站长的生存空间是否越来越小?
  • 弥补两个不足来提升企业站流量
  • 中国象棋程序的设计与实现(高级版)(2012本科毕业论文等重要文档资料)
  • linux arping命令学习
  • linux的多任务编程-线程池
  • 裸设备上的oracle文件备份-----HP-UX下oracle的裸设备大小
  • Oracle 11.2.0.2.0 RAC环境一次内存溢出ORA-04031问题的处理
  • CSS3 聊天气泡框以及 inherit、currentColor 关键字
  • dva中组件的懒加载
  • Electron入门介绍
  • Git同步原始仓库到Fork仓库中
  • js学习笔记
  • mysql innodb 索引使用指南
  • node和express搭建代理服务器(源码)
  • 从零开始的webpack生活-0x009:FilesLoader装载文件
  • 如何进阶一名有竞争力的程序员?
  • 扫描识别控件Dynamic Web TWAIN v12.2发布,改进SSL证书
  • Nginx惊现漏洞 百万网站面临“拖库”风险
  • ​【已解决】npm install​卡主不动的情况
  • ​LeetCode解法汇总307. 区域和检索 - 数组可修改
  • # 执行时间 统计mysql_一文说尽 MySQL 优化原理
  • #### go map 底层结构 ####
  • #vue3 实现前端下载excel文件模板功能
  • #鸿蒙生态创新中心#揭幕仪式在深圳湾科技生态园举行
  • #我与Java虚拟机的故事#连载17:我的Java技术水平有了一个本质的提升
  • (06)Hive——正则表达式
  • (day6) 319. 灯泡开关
  • (Oracle)SQL优化技巧(一):分页查询
  • (第8天)保姆级 PL/SQL Developer 安装与配置
  • (求助)用傲游上csdn博客时标签栏和网址栏一直显示袁萌 的头像
  • (三十五)大数据实战——Superset可视化平台搭建
  • (原創) 如何使用ISO C++讀寫BMP圖檔? (C/C++) (Image Processing)
  • .Net core 6.0 升8.0
  • .NET设计模式(7):创建型模式专题总结(Creational Pattern)
  • ;号自动换行
  • @Valid和@NotNull字段校验使用
  • [ linux ] linux 命令英文全称及解释
  • [Android Pro] Notification的使用
  • [Android]使用Android打包Unity工程
  • [asp.net core]project.json(2)
  • [BZOJ1010] [HNOI2008] 玩具装箱toy (斜率优化)
  • [BZOJ4554][TJOI2016HEOI2016]游戏(匈牙利)
  • [CareerCup] 17.8 Contiguous Sequence with Largest Sum 连续子序列之和最大
  • [codeforces]Recover the String
  • [GN] Vue3.2 快速上手 ---- 核心语法2
  • [Golang]K-V存储引擎的学习 从零实现 (RoseDB mini版本)
  • [HCIE] IPSec-VPN (手工模式)