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

OC-KVC

文章目录

  • 定义
    • 四个方法
    • 用法
      • key
      • keyPath
    • 设值流程
    • 验证
  • 处理不存在的key
  • 处理nil值
  • KVC处理数值和结构体类型
    • NSNumber
    • NSValue
      • 简介
      • 类型
      • 使用
  • KVC键值验证
  • KVC处理集合
    • 简单集合运算符
    • 对象运算符
  • KVC处理字典
    • 两个方法
  • KVC应用

定义

KVC键值编码是指iOS开发中允许作者通过key名直接访问对象的属性或者赋值,而不是明确调用存取方法。使用KVC键值编码可以在运行时动态的访问和修改对象的属性,而不是在编译时确定

注:在实现了访问器方法的类中使用点语法与KVC访问对象差别不大。但是在没有实现访问器方法的类中,适合使用KVC

KVC是基于NSObject的扩展来实现的,OC语言中有一个显示NSKeyValueCoding类别名,所以所有继承了NSObject的类型都可以使用KVC。除了一些没有继承NSObject的纯Swift类和结构体不支持KVC。

四个方法

- (void)setValue:(id)value forKeyPath:(NSString* )keyPath;
- (void)setValue:(id)value forKey:(NSString* )key;
- (id)valueForKeyPath:(NSString* )keyPath;
- (id)valueForKey:(NSString* )key;
  • key和keyPath的区别
    • key只接受当前类所具有的属性,不管是自己的还是从父类继承过来的
    • keyPath:除了能接受当前类的属性,还可以接受当前类的属性的属性(接受关系链)

用法

key

 Coffee* coffee = [[Coffee alloc] init];[coffee setValue:@"10000" forKey:@"price"];NSLog(@"%@", [coffee valueForKey:@"price"]);

输出结果如下:
在这里插入图片描述

keyPath

coffee.sonOfCoffee = [[SonOfCoffee alloc] init];[coffee setValue:@"white" forKeyPath:@"sonOfCoffee.color"];NSLog(@"%@", [coffee.sonOfCoffee valueForKey:@"color"]);

输出结果如下:
请添加图片描述

设值流程

  1. 按照setKey、_setKey的顺序查找方法,如果找到直接赋值
  2. 没有找到的话,则调用
+ (BOOL)accessInstanceVariablesDirectly {return YES;
/*默认返回YES表示如果没有找到Set<Key>这个存取方法的话,将按照_key、_iskey、key、iskey的顺序搜索成员,如果重写方法设置为NO,在没有找到Set<Key>后直接停止搜索了。
*/
}
  1. KVC机制会去寻找该类中以传入的"_该字符串"的成员变量,大部分时间即属性创建时自动生成的成员变量,无论该成员变量是在接口部分还是实现部分定义,无论使用的是那个访问控制符修饰,这条KVC底层都是对其进行赋值
  2. 如果仍然没有找到,系统会执行该对象的setValue:forUndefinedKey:方法,这个方法会引发一个异常,导致程序结束,valueForKey方法类似,但是获取的是getter方法的返回值,没有找到成员变量会执行valueForUndefinedKey:方法,同义也会导致·程序终止

验证

#import <Foundation/Foundation.h>NS_ASSUME_NONNULL_BEGIN@interface AUser : NSObject {@packageNSString* name;NSString* _name;
}@endNS_ASSUME_NONNULL_END
//  Created by xiaoli pop on 2025/9/10.
//#import "AUser.h"@implementation AUser {int age;
}@end
//  Created by xiaoli pop on 2025/9/9.
//#import <Foundation/Foundation.h>
#import "AUser.h"
int main(int argc, const char * argv[]) {@autoreleasepool {AUser* aUser = [[AUser alloc] init];[aUser setValue:@"strName1" forKey:@"name"];NSLog(@"name = %@", aUser->name);NSLog(@"_name = %@", aUser->_name);[aUser setValue:[NSNumber numberWithInt:10] forKey:@"age"];NSLog(@"age = %@", [aUser valueForKey:@"age"]);}return 0;
}

我们运行发现结果如下:
在这里插入图片描述

我们解释一下原因:首先我们使用KVC给属性赋值,首先查找set方法,没有找到,便按照_name、name的属性去查找。先搜索到的是_name,所以给_name赋值,name未被赋值。所以name为空。对于age成员变量来说,尽管是在类的实现部分定义的,也被赋值了

处理不存在的key

我们在使用KVC时,如果该属性没有setter、getter方法,也不存在对应的成员变量时,程序会调用setValue:forUndefinedKey: 或者valueForUndefinedKey:方法。系统默认该方法的实现是引发一个异常导致程序终止,但是我们可以重写这个方法,达到我们想要的效果


#import "AUser.h"@implementation AUser {int age;
}-(void)setValue:(id)value forUndefinedKey:(NSString *)key {NSLog(@"重写了setValue: forundefinedKey:方法");
}@end
#import <Foundation/Foundation.h>
#import "AUser.h"
int main(int argc, const char * argv[]) {@autoreleasepool {AUser* aUser = [[AUser alloc] init];[aUser setValue:@"nnnn" forKey:@"123"];}return 0;
}

输出如下结果:
在这里插入图片描述

处理nil值

假如我们在一个类中定义一个int类型的属性时,如果给他赋值为nil的话,会引发异常,因为int类型是不支持接受nil值的,也就是说,当程序尝试给某个属性赋nil时,如果该属性不能接受nil值,程序就会自动执行该对象的setNilValueForKey:方法。我们同样可以重写方法来达到我们想要的效果。示例如下:

#import "AUser.h"@implementation AUser {int age;
}- (void)setNilValueForKey:(NSString *)key {if ([key isEqualToString:@"age"]) {age = 0;} else {[super setNilValueForKey:key];}
}
//  Created by xiaoli pop on 2025/9/9.
//#import <Foundation/Foundation.h>
#import "AUser.h"
int main(int argc, const char * argv[]) {@autoreleasepool {AUser* aUser = [[AUser alloc] init];[aUser setValue:nil forKey:@"age"];NSLog(@"age = %@", [aUser valueForKey:@"age"]);}return 0;
}

输出结果如下:
在这里插入图片描述

KVC处理数值和结构体类型

valueForKey:方法总会返回一个id对象,如果原本的类型变量是值类型或者结构体类型,返回值会封装成NSNumber或者NSValue对象,这两个类会处理数字、布尔值和结构体。
在使用setValue:forKey:时,我们需要手动将值类型转换为NSNumber或者NSValue类型才能传过去。因为传入和传出都是id类型,所以需要自己保证类型正确,运行时如果错会抛出异常

NSNumber

可以使用NSNumber的数据类型:

+ (NSNumber *)numberWithChar:(char)value;
+ (NSNumber *)numberWithUnsignedChar:(unsigned char)value;
+ (NSNumber *)numberWithShort:(short)value;
+ (NSNumber *)numberWithUnsignedShort:(unsigned short)value;
+ (NSNumber *)numberWithInt:(int)value;
+ (NSNumber *)numberWithUnsignedInt:(unsigned int)value;
+ (NSNumber *)numberWithLong:(long)value;
+ (NSNumber *)numberWithUnsignedLong:(unsigned long)value;
+ (NSNumber *)numberWithLongLong:(long long)value;
+ (NSNumber *)numberWithUnsignedLongLong:(unsigned long long)value;
+ (NSNumber *)numberWithFloat:(float)value;
+ (NSNumber *)numberWithDouble:(double)value;
+ (NSNumber *)numberWithBool:(BOOL)value;
+ (NSNumber *)numberWithInteger:(NSInteger)value API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));
+ (NSNumber *)numberWithUnsignedInteger:(NSUInteger)value API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));

NSValue

简介

NSValue是Foundation框架的一部分,用于封装基本数据类型,如结构体和枚举,允许我们将基本数据类型存储在对象中。

类型

+ (NSValue*)valueWithCGPoint:(CGPoint)point;
+ (NSValue*)valueWithCGSize:(CGSize)size;
+ (NSValue*)valueWithCGRect:(CGRect)rect;
+ (NSValue*)valueWithCGAffineTransform:(CGAffineTransform)transform;
+ (NSValue*)valueWithUIEdgeInsets:(UIEdgeInsets)insets;
+ (NSValue*)valueWithUIOffset:(UIOffset)insetsNS_AVAILABLE_IOS(5_0);

任何结构体都可以都可以转化为NSValue对象,包括自定义的结构体

使用

我们可以使用valueWithBytes:objCType:方法来创建一个NSValue对象,这个方法接受两个参数,一个是指向数据的指针,另一个是描述数据类型的字符串

//  Created by xiaoli pop on 2025/9/9.
//#import <Foundation/Foundation.h>
#import "AUser.h"
int main(int argc, const char * argv[]) {@autoreleasepool {CGPoint point = CGPointMake(54, 45);NSValue* pointValue = [NSValue valueWithBytes:&point objCType:@encode(CGPoint)];/*@encode()返回一个OC类型的字符串编码,可以是基本数据类型,也可以是结构体类型。*/NSArray* array = [NSArray arrayWithObject:pointValue];NSLog(@"%@", array);CGRect rect = CGRectMake(10, 10, 10, 10);NSValue* pointRect = [NSValue valueWithBytes:&rect objCType:@encode(CGRect)];NSArray* array2 = [NSArray arrayWithObject:pointRect];NSLog(@"%@", array2);}return 0;
}

KVC键值验证

- (BOOL)validateValue:(inout id  _Nullable __autoreleasing *)ioValue forKey:(NSString *)inKey error:(out NSError *__autoreleasing  _Nullable *)outError {if ([inKey isEqualToString:@"name"]) {NSString* name = (NSString* )*ioValue;if (name.length == 0) {if (outError) {*outError = [NSError errorWithDomain:@"MyDomain" code:1001 userInfo:@{NSLocalizedDescriptionKey:@"NAme cannot be empty."}];}return NO;}}return YES;
}

我们通过重写这个方法,就可以对属性值进行验证

#import <Foundation/Foundation.h>
#import "AUser.h"
int main(int argc, const char * argv[]) {@autoreleasepool {NSError* error = nil;NSString* name = @"";AUser* aUser = [[AUser alloc] init];if (![aUser validateValue:&name forKey:@"name" error:&error]) {NSLog(@"Validation failed: %@", error.localizedDescription);} else {[aUser setValue:name forKey:@"name"];}}return 0;
}

输出结果如下:
在这里插入图片描述

KVC处理集合

简单集合运算符

简单的集合运算符:
@avg:平均值
@count:总个数
@max: 最大值
@min:最小值
@sum:总量


#import <Foundation/Foundation.h>
#import "AUser.h"
#import "BUser.h"
int main(int argc, const char * argv[]) {@autoreleasepool {BUser* b1 = [BUser new];b1.name = @"小李";b1.age = 10;BUser* b2 = [BUser new];b2.name = @"小吴";b2.age = 20;BUser* b3 = [BUser new];b3.name = @"小杨";b3.age = 30;NSArray* array = [NSArray arrayWithObjects:b1, b2, b3, nil];NSNumber* sum = [array valueForKeyPath:@"@sum.age"];NSLog(@"sum = %@", sum);NSNumber* avg = [array valueForKeyPath:@"@avg.age"];NSLog(@"avg = %@", avg);NSNumber* count = [array valueForKeyPath:@"@count.age"];NSLog(@"count = %@", count);NSNumber* min = [array valueForKeyPath:@"@min.age"];NSLog(@"min = %@", min);NSNumber* max = [array valueForKeyPath:@"@max.age"];NSLog(@"max = %@", max);}return 0;
}

输出结果如下:
在这里插入图片描述

对象运算符

以数组的形似返回。有下面几种情况

  1. unionOfObjects: 不去重
  2. distinctUnionOfObjects: 去重
  3. unionOfArrays:不去重
  4. distinctUnionOfArrays:去重
    后面两种是针对二维数组的
    示例:
#import <Foundation/Foundation.h>
#import "AUser.h"
#import "BUser.h"
int main(int argc, const char * argv[]) {@autoreleasepool {BUser* b1 = [BUser new];b1.name = @"小李";b1.age = 10;BUser* b2 = [BUser new];b2.name = @"小吴";b2.age = 20;BUser* b3 = [BUser new];b3.name = @"小杨";b3.age = 30;NSArray* array = [NSArray arrayWithObjects:b1, b2, b3, nil];NSArray* nameArray = [array valueForKeyPath:@"@unionOfObjects.name"];for (NSString* nameStr in nameArray) {NSLog(@"%@",nameStr);}}return 0;
}

输出结果如下:
在这里插入图片描述

KVC处理字典

两个方法

- (NSDictionary<NSString *,id> *)dictionaryWithValuesForKeys:(NSArray<NSString *> *)keys {//输入一组key,返回这组key对应的属性,再组成一个字典返回
}
- (void)setValuesForKeysWithDictionary:(NSDictionary<NSString *,id> *)keyedValues {//用来修改Model中对应的属性
}

示例:

@property (nonatomic, copy)NSString* country;
@property (nonatomic, copy)NSString* province;
@property (nonatomic, copy)NSString* city;
@property (nonatomic, copy)NSString* district;
int main(int argc, const char * argv[]) {@autoreleasepool {BUser* bUser = [BUser new];bUser.country = @"China";bUser.province = @"ShanXi";bUser.city = @"Ankang";bUser.district = @"HanBin";NSArray* array = @[@"country", @"province", @"city", @"district"];NSDictionary* dict = [bUser dictionaryWithValuesForKeys:array];NSLog(@"%@", dict);NSDictionary* modifyDict = @{@"country" : @"USA", @"province" : @"califonia", @"city" : @"ooo", @"district" : @"ppp"};[bUser setValuesForKeysWithDictionary:modifyDict];NSLog(@"country = %@, province = %@ , city = %@", bUser.country, bUser.province, bUser.city);}return 0;
}

在这里插入图片描述

KVC应用

  1. 动态的取值设值
  2. 使用KVC访问和修改私有变量
  3. Model和字典之间的转化
  4. 修改一些控件的内部属性
  5. 操作集合

文章转载自:

http://4nSCoEGD.nrgdc.cn
http://u5PzsvUD.nrgdc.cn
http://7dxUIS9A.nrgdc.cn
http://QZxKG8gI.nrgdc.cn
http://LxZ6hGec.nrgdc.cn
http://mQVpxoo8.nrgdc.cn
http://sWlrXfZK.nrgdc.cn
http://p67kISwh.nrgdc.cn
http://EiUb51wG.nrgdc.cn
http://TPOPdjuZ.nrgdc.cn
http://Eux8J3zy.nrgdc.cn
http://8gjzVABf.nrgdc.cn
http://FZZp8dTZ.nrgdc.cn
http://6K897P8D.nrgdc.cn
http://Qb1I4cZe.nrgdc.cn
http://El4I0lW0.nrgdc.cn
http://4uWX5sVe.nrgdc.cn
http://kKKaDXeo.nrgdc.cn
http://w7CLBTEV.nrgdc.cn
http://NlFH56Yg.nrgdc.cn
http://RnA3A6TQ.nrgdc.cn
http://BSV4mENz.nrgdc.cn
http://cUFLhGYQ.nrgdc.cn
http://2w2tbDaM.nrgdc.cn
http://eqKpDn85.nrgdc.cn
http://8yaPSkCZ.nrgdc.cn
http://jFmGl0DP.nrgdc.cn
http://IigMiAn4.nrgdc.cn
http://yTN7e6LH.nrgdc.cn
http://taONVDtj.nrgdc.cn
http://www.dtcms.com/a/379027.html

相关文章:

  • Linux系统编程—基础IO
  • 考研408计算机网络2023-2024年第33题解析
  • 手眼标定之已知同名点对,求解转换RT,备份记录
  • 《MySQL事务问题与隔离级别,一篇讲透核心考点》
  • 水泵自动化远程监测与控制的御控物联网解决方案
  • Bug排查日记的技术
  • AR眼镜:化工安全生产的技术革命
  • 跨越符号的鸿沟——认知语义学对人工智能自然语言处理的影响与启示
  • 深入理解大语言模型(5)-关于token
  • Node.js-基础
  • JVM垃圾回收的时机是什么时候(深入理解 JVM 垃圾回收时机:什么时候会触发 GC?)
  • Python 版本和Quantstats不兼容的问题
  • SFINAE
  • TCP 三次握手与四次挥手
  • 【iOS】UIViewController生命周期
  • 硬件开发(7)—IMX6ULL裸机—led进阶、SDK使用(蜂鸣器拓展)、BSP工程目录
  • 人工智能学习:Transformer结构中的编码器层(Encoder Layer)
  • RISCV中PLIC和AIA的KVM中断处理
  • 掌握梯度提升:构建强大的机器学习模型介绍
  • 全球智能电网AI加速卡市场规模到2031年将达20216百万美元
  • springbook3整合Swagger
  • LMS 算法:抗量子时代的「安全签名工具」
  • CUDA中thrust::device_vector使用详解
  • Python学习-day8 元组tuple
  • 2025主流大模型核心信息
  • skywalking定位慢接口调用链路的使用笔记
  • LeetCode刷题记录----739.每日温度(Medium)
  • eNSP华为无线网测试卷:AC+AP,旁挂+直连
  • 开源多模态OpenFlamingo横空出世,基于Flamingo架构实现图像文本自由对话,重塑人机交互未来
  • 光路科技将携工控四大产品亮相工博会,展示工业自动化新成果