[iOS开发]KVC
Posted Billy Miracle
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[iOS开发]KVC相关的知识,希望对你有一定的参考价值。
Objective-C支持一种灵活的操作方式,这种方式允许以字符串形式间接操作对象的属性,这种方式的全称是Key Value Coding(KVC),即键值编码。
简单的KVC
最基本的KVC由NSKeyValueCoding协议提供支持,最基本的操作属性的两个方法如下:
- setValue:属性值forKey:属姓名:为指定的属性设定值
- valueForKey:属姓名:获取指定属性的值
示例:
// Dog.h
#import <Foundation/Foundation.h>
#import "Bone.h"
NS_ASSUME_NONNULL_BEGIN
@class Bone;
@interface Dog : NSObject {
@package
NSString *gender;
NSString *_gender;
}
@property (nonatomic, copy) NSString *name;
@property (nonatomic, strong) Bone *bone;
@property (nonatomic, assign) int weight;
@end
// Bone.h
#import <Foundation/Foundation.h>
@interface Bone : NSObject
@property (nonatomic, strong) NSString *type;
@property (nonatomic, assign) NSInteger weight;
@end
//若dog声明成属性
_dog = [[Dog alloc] init];
_dog.bone = [[Bone alloc] init];
[_dog setValue:@"jack" forKey:@"name"];
// [self setValue:@"jake" forKeyPath:@"self.dog.name"];
[_dog.bone setValue:@"猪骨" forKey:@"type"];
// [self setValue:@"猪骨" forKeyPath:@"_dog.bone.type"];
NSLog(@"%@", [_dog valueForKey:@"name"]);
NSLog(@"%@", [_dog.bone valueForKey:@"type"]);
输出:
jake
猪骨
在KVC编程方式中,无论调用setValue:forKey:方法,还是调用valueForKey:方法,都是通过NSString对象来指定被操作属性的,其中forKey标签用于传入属性名。
对于“setValue: forKey:”代码,底层的执行机制如下:
- 程序优先考虑调用“setName:属性值”代码通过setter方法完成设置。
- 如果该类没有setName:方法,那么KVC机制会搜索该类中名为_name的成员变量,无论该成员变量在类的接口部分定义,还是在实现部分定义,也无论用哪个访问控制符修饰,这条KVC代码底层实际上就是对_name成员变量赋值。
- 如果该类既没有setName:方法,也没有定义_name成员变量,那么KVC机制会搜索该类中名为name的成员变量,无论该成员变量在类的接口部分定义,还是在实现部分定义,也无论用哪个访问控制符修饰,这条KVC代码底层实际上就是对name成员变量赋值。
- 如果上面三步都没有找到,那么系统将会执行该对象的setValue:forUndefinedKey:方法。默认的setValue:forUndefinedKey:方法实现就是引发一个异常,这个异常将会导致程序因为异常结束。
对于“valueforKey:”代码,底层的执行机制如下:
- 程序优先考虑调用“name”代码来获取getter方法返回值。
- 如果该类没有name:方法,那么KVC机制会搜索该类中名为_name的成员变量,无论该成员变量在类的接口部分定义,还是在实现部分定义,也无论用哪个访问控制符修饰,这条KVC代码底层实际上就是返回_name成员变量的值。
- 如果该类既没有name:方法,也没有定义_name成员变量,那么KVC机制会搜索该类中名为name的成员变量,无论该成员变量在类的接口部分定义,还是在实现部分定义,也无论用哪个访问控制符修饰,这条KVC代码底层实际上就是返回name成员变量的值。
- 如果上面三步都没有找到,那么系统将会执行该对象的valueForUndefinedKey:方法。默认的valueForUndefinedKey:方法实现就是引发一个异常,这个异常将会导致程序因为异常结束。
// Dog.m
//类实现部分定义age
#import "Dog.h"
@implementation Dog {
int age;
}
@end
[_dog setValue:@5 forKey:@"age"];
NSLog(@"%@", [_dog valueForKey:@"age"]);
KVC可以正常工作:输出5。
[_dog setValue:@"f" forKey:@"gender"];
NSLog(@"%@", _dog->gender);
NSLog(@"%@", _dog->_gender);
输出:
(null)
f
处理不存在的key
可以在实现文件中重写setValue: forUndefinedKey:方法与valueForUndefinedKey:方法来实现一些自定义行为:
- (id) valueForUndefinedKey:(NSString *)key {
NSLog(@"尝试访问的key:【%@】并不存在", key);
return 0;
}
- (void)setValue:(id)value forUndefinedKey:(NSString *)key {
NSLog(@"尝试设置的key:【%@】并不存在", key);
NSLog(@"尝试设置的value:%@", value);
}
[_dog setValue:@"no" forKey:@"hhh"];
[_dog valueForKey:@"hhh"];
输出:
尝试设置的key:【hhh】并不存在
尝试设置的value:no
尝试访问的key:【hhh】并不存在
处理nil值
可以在实现文件中重写setNilValue:forKey:方法来实现一些自定义行为:
- (void)setNilValueForKey:(NSString *)key {
if ([key isEqualToString:@"weight"]) {
//如果尝试将key为weight属性设置为nil
//将_weight设为0
_weight = 0;
} else {
[super setNilValueForKey:key];
}
}
[_dog setValue:nil forKey:@"weight"];
NSLog(@"%d", _dog.weight);
key路径
KVC除了可操作对象的属性之外,还可以操作对象的“复合属性”。所谓复合属性,KVC机制将其称为key路径。
在KVC协议中操作key路径的方法如下:
- setValue: forKeyPath:
//若dog声明成属性
_dog = [[Dog alloc] init];
_dog.bone = [[Bone alloc] init];
// [_dog setValue:@"jack" forKey:@"name"];
[self setValue:@"jake" forKeyPath:@"self.dog.name"];
// [_dog.bone setValue:@"猪骨" forKey:@"type"];
[self setValue:@"猪骨" forKeyPath:@"_dog.bone.type"];
NSLog(@"%@", [_dog valueForKeyPath:@"name"]);
NSLog(@"%@", [self valueForKeyPath:@"_dog.bone.type"]);
输出:
jake
猪骨
此外,KVC还在其他很多方面有作用:
//对数组内的字典相同的key取值
NSDictionary *dic1 = @{@"city":@"北京",@"count":@"22"};
NSDictionary *dic2 = @{@"city":@"上海",@"count":@"18"};
NSDictionary *dic3 = @{@"city":@"深圳",@"count":@"17"};
NSArray *arr = @[dic1,dic2,dic3];
NSLog(@"city:%@",[arr valueForKeyPath:@"city"]);
NSLog(@"count:%@",[arr valueForKeyPath:@"count"]);
//数组求和sum、求平均average、求最大值max、求最小值min
NSArray *array = @[@20, @10, @30, @4, @6, @"8"];
CGFloat sumValue = [[array valueForKeyPath:@"@sum.floatValue"] floatValue];
NSLog(@"%f", sumValue); // 78
//第一个例子
NSLog(@"求和:%@",[arr valueForKeyPath:@"@sum.count"]);
NSLog(@"平均:%@",[arr valueForKeyPath:@"@avg.count"]);
NSLog(@"最大:%@",[arr valueForKeyPath:@"@max.count"]);
NSLog(@"最小:%@",[arr valueForKeyPath:@"@min.count"]);
//数组内字符串操作
NSArray *arrayString = @[@"a", @"bb", @"ccc"];
NSArray *tmp = [arrayString valueForKeyPath:@"uppercaseString"];
NSArray *tmp2 = [arrayString valueForKey:@"uppercaseString"];
//valueForKey也可以达到相同目的
NSArray *lengths = [arrayString valueForKeyPath:@"length"];
NSLog(@"%@", lengths);
NSLog(@"%@", tmp);
//数组去重
NSArray *array2 = @[@"1", @"dd", @"ccc", @"dd", @"1", @"d", @"d"];
NSArray *rs = [array2 valueForKeyPath:@"@distinctUnionOfObjects.self"];
NSLog(@"%@", rs);
//数组内包含字典,去重字典某字段的重复值
NSArray *array3 = @[@{@"name": @"zhangsan", @"age": @"1"},
@{@"name": @"zhangsan", @"age": @"1"},
@{@"name": @"lisi", @"age": @"2"}];
rs = [array3 valueForKeyPath:@"@distinctUnionOfObjects.name"];
NSLog(@"%@", rs);
NSArray *rs2 = [array3 valueForKeyPath:@"@distinctUnionOfObjects.age"];
NSLog(@"%@", rs2);
//多级字典嵌套取值
//对于 @{key1:@{key2:vale}} 的字典(字典的value是另一个字典),通过 key1.key2 的链式的方式得到最深层的字典的值。
NSDictionary *dict4 = @{@"name":@"小明",@"age":@"22"};
NSDictionary *dict5 = @{@"student":dict4};
NSDictionary *dict6 = @{@"class":dict5};
NSDictionary *dict7 = @{@"school":dict6};
NSLog(@"%@",[dict7 valueForKeyPath:@"school.class.student.name"]);
NSLog(@"%@",[dict7 valueForKeyPath:@"school.class.student.age"]);
//数组内模型取值
NSArray *array4 = @[@"a", @"bb", @"ccc"];
NSMutableArray *Bones = [NSMutableArray arrayWithCapacity:array4.count];
for (int i = 0; i < array4.count; i++) {
Bone *b = [[Bone alloc]init];
b.type = array4[i];
b.weight = i;
[Bones addObject:b];
}
NSArray *names = [Bones valueForKeyPath:@"type"];
NSLog(@"%@", names);
输出结果:
city:(
"\\U5317\\U4eac",
"\\U4e0a\\U6d77",
"\\U6df1\\U5733"
)
count:(
22,
18,
17
)
78.000000
求和:57
平均:19
最大:22
最小:17
(
1,
2,
3
)
(
A,
BB,
CCC
)
(
dd,
d,
1,
ccc
)
(
zhangsan,
lisi
)
(
1,
2
)
小明
22
(
a,
bb,
ccc
)
小结:
通过KVC操作对象的性能比通过setter、getter方法操作的性能更差,使用KVC的优势在于编程更加灵活,更适合提炼一些通用性质的代码。由于KVC方式允许通过字符串形式来操作对象的属性,这个字符串既可以是常量,也可以是变量,因此具有极高的灵活性。
以上是关于[iOS开发]KVC的主要内容,如果未能解决你的问题,请参考以下文章