iOS -- KVC高级使用以及赋值过程分析
一、KVC赋值过程分析
1.1. 先找相关方法 set<Key>:
_set<Key>:
setIs<Key>:
1.2. 若没有相关方法+ (BOOL)accessInstanceVariablesDirectly
判断是否可以直接访问成员变量(默认为YES
)
1.3. 如果是判断是NO
,直接执行KVC
的setValue:forUndefinedKey:
(系统抛出一个异常,未定义key
)
1.4. 如果是YES
,继续找相关变量_<key>
_is<Key>
<key>
is<Key>
1.5. 方法或成员都不存在,setValue: forUndefinedKey:
方法,默认是抛出异常
示例1
// ZYPerson.h
@interface ZYPerson : NSObject{
@public
NSString* _name; ④
NSString* _isName; ⑤
NSString* name; ⑥
NSString* isName; ⑦
}
#import "ZYPerson.h"
// ZYPerson.m
@implementation ZYPerson
- (void) setName:(NSString*) name { ①
NSLog(@"%s", __func__);
}
- (void) _setName:(NSString*) name { ②
NSLog(@"%s", __func__);
}
- (void) setIsName:(NSString*) name { ③
NSLog(@"%s", __func__);
}
- (NSString*) getName { ⑧
NSLog(@"%s", __func__);
return @"getName";
}
- (NSString*) name { ⑨
NSLog(@"%s", __func__);
return @"name";
}
@end
// ViewController.m
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
ZYPerson *p = [ZYPerson new];
[p setValue:@"tom" forKey:@"name"];
NSLog(@"name = %@", [p valueForKey:@"name"]);
NSLog(@"name = %@", p->name);
NSLog(@"_name = %@", p->_name);
NSLog(@"isName = %@", p->isName);
NSLog(@"_isName = %@", p->_isName);
}
@end
说明 ① ② ③ 等代码中标注
如果①存在,② ③ 可无,结果如下
2019-01-16 15:46:56.798680+0800 KVC001[20175:1536368] -[ZYPerson setName:]
2019-01-16 15:46:56.798798+0800 KVC001[20175:1536368] name = (null)
2019-01-16 15:46:56.798881+0800 KVC001[20175:1536368] _name = (null)
2019-01-16 15:46:56.798955+0800 KVC001[20175:1536368] isName = (null)
2019-01-16 15:46:56.799023+0800 KVC001[20175:1536368] _isName = (null)
如果①不存在,② 存在,③ 可无,结果如下
2019-01-16 15:49:26.650198+0800 KVC001[20220:1539317] -[ZYPerson _setName:]
2019-01-16 15:49:26.650333+0800 KVC001[20220:1539317] name = (null)
2019-01-16 15:49:26.650412+0800 KVC001[20220:1539317] _name = (null)
2019-01-16 15:49:26.650482+0800 KVC001[20220:1539317] isName = (null)
2019-01-16 15:49:26.650548+0800 KVC001[20220:1539317] _isName = (null)
如果① ②不存在,③ 存在,结果如下
2019-01-16 15:50:19.981200+0800 KVC001[20246:1540579] -[ZYPerson setIsName:]
2019-01-16 15:50:19.981313+0800 KVC001[20246:1540579] name = (null)
2019-01-16 15:50:19.981399+0800 KVC001[20246:1540579] _name = (null)
2019-01-16 15:50:19.981475+0800 KVC001[20246:1540579] isName = (null)
2019-01-16 15:50:19.981541+0800 KVC001[20246:1540579] _isName = (null)
如果① ② ③ 不存在,结果如下
2019-01-16 15:57:59.461807+0800 KVC001[20377:1549518] name = (null)
2019-01-16 15:57:59.461938+0800 KVC001[20377:1549518] _name = tom
2019-01-16 15:57:59.462019+0800 KVC001[20377:1549518] isName = (null)
2019-01-16 15:57:59.462112+0800 KVC001[20377:1549518] _isName = (null)
如果① ② ③ ④不存在,结果如下
2019-01-16 15:59:34.276391+0800 KVC001[20413:1551470] name = (null)
2019-01-16 15:59:34.276510+0800 KVC001[20413:1551470] isName = (null)
2019-01-16 15:59:34.276587+0800 KVC001[20413:1551470] _isName = tom
以此类推...
结论: 先找相关方法 set<Key>:
_set<Key>:
setIs<Key>:
再找相关变量_<key>
_is<Key>
<key> is<Key>
二、KVC取值过程分析
2.1. 先找相关方法get<Key>
key
2.2. 若没有相关方法 + (BOOL)accessInstanceVariablesDirectly
,判断是否可以直接方法成员变量(默认是YES
)
2.3. 如果是判断是NO
,直接执行KVC
的valueForUndefinedKey:
(系统抛出一个异常,未定义key
)
2.4. 如果是YES
,继续找相关变量_<key>
_is<Key>
<key>
is<Key>
2.5. 方法或成员都不存在,valueForUndefinedKey:
方法,默认是抛出异常
如果⑧存在,⑨可无,结果如下
2019-01-16 16:18:45.537583+0800 KVC001[20710:1573310] -[ZYPerson getName]
2019-01-16 16:18:45.537709+0800 KVC001[20710:1573310] name = getName
2019-01-16 16:18:45.537797+0800 KVC001[20710:1573310] name = (null)
2019-01-16 16:18:45.537869+0800 KVC001[20710:1573310] isName = (null)
2019-01-16 16:18:45.537941+0800 KVC001[20710:1573310] _isName = tom
如果⑧ 不存在,⑨存在,结果如下
2019-01-16 16:20:15.113493+0800 KVC001[20744:1575289] -[ZYPerson name]
2019-01-16 16:20:15.113627+0800 KVC001[20744:1575289] name = name
2019-01-16 16:20:15.113708+0800 KVC001[20744:1575289] name = (null)
2019-01-16 16:20:15.113778+0800 KVC001[20744:1575289] isName = (null)
2019-01-16 16:20:15.114321+0800 KVC001[20744:1575289] _isName = tom
如果⑧ ⑨不存在,结果如下
2019-01-16 16:22:04.841822+0800 KVC001[20792:1578120] name = tom
2019-01-16 16:22:04.841938+0800 KVC001[20792:1578120] name = (null)
2019-01-16 16:22:04.842009+0800 KVC001[20792:1578120] _name = tom
2019-01-16 16:22:04.842078+0800 KVC001[20792:1578120] isName = (null)
2019-01-16 16:22:04.842144+0800 KVC001[20792:1578120] _isName = (null)
以此类推...
结论: 先找相关方法get<Key>
key
再找相关变量_<key> _is<Key>
<key> is<Key>
三、KVC异常处理
示例2
// ZYPerson.h
@interface ZYPerson : NSObject
@property (nonatomic, copy) NSString *name;
@property (nonatomic, assign) NSInteger age;
@end
// ViewController.m
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
ZYPerson *p = [ZYPerson new];
[p setValue:nil forKey:@"age"];
[p setValue:@"tom" forKey:@"name1"];
NSLog(@"name = %@", [p valueForKey:@"name1"]);
}
@end
3.1、值为空
2019-01-16 16:46:44.065665+0800 KVC001[21151:1602368] *** Terminating app due to uncaught
exception 'NSInvalidArgumentException',
reason: '[<ZYPerson 0x600000dfe360> setNilValueForKey]:
could not set nil as the value for the key age.'
*** First throw call stack:
3.2、key
值不存在
3.2.1、赋值key
值不存在
2019-01-16 17:05:38.681366+0800 KVC001[21451:1622468] *** Terminating app due to uncaught
exception 'NSUnknownKeyException', r
eason: '[<ZYPerson 0x6000001664e0> setValue:forUndefinedKey:]:
this class is not key value coding-compliant for the key name1.'
*** First throw call stack:
3.2.2、取值key
值不存在
2019-01-16 17:16:26.100903+0800 KVC001[21604:1634947] *** Terminating app due to uncaught
exception 'NSUnknownKeyException',
reason: '[<ZYPerson 0x600002d09300> valueForUndefinedKey:]: this class is not
key value coding-compliant for the key name1.'
*** First throw call stack:
为了程序的健壮性,重写上面报错方法,如下:
// ZYPerson.m
@implementation ZYPerson
// 对非对象类型,值不能为空
- (void) setNilValueForKey:(NSString *)key {
NSLog(@"%@ 值不能为空", key);
}
// 赋值key值不存在
- (void) setValue:(id)value forUndefinedKey:(NSString *)key {
NSLog(@"key = %@值不存在 ", key);
}
// 取值key值不存在
- (id) valueForUndefinedKey:(NSString *)key {
NSLog(@"key=%@不存在", key);
return nil;
}
@end
结果如下:
2019-01-16 17:38:24.760843+0800 KVC001[21893:1654601] age 值不能为空
2019-01-16 17:38:24.761428+0800 KVC001[21893:1654601] key = name1值不存在
2019-01-16 17:38:24.761934+0800 KVC001[21893:1654601] key=name1不存在
2019-01-16 17:38:24.762044+0800 KVC001[21893:1654601] name = (null)
四、KVC正确性验证
场景:假设人的年纪不能小于0或者不能大于200
// ViewController.m
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
ZYPerson *p = [ZYPerson new];
NSNumber* value = @200;
if ([p validateValue:&value forKey:@"age" error:NULL]) {
[p setValue:value forKey:@"age"];
}
}
// ZYPerson.m
@implementation ZYPerson
- (BOOL) validateAge:(inout id _Nullable __autoreleasing *)ioValue error:(out NSError * _Nullable __autoreleasing *)outError {
NSNumber* value = (NSNumber*)*ioValue;
if ([value integerValue] <= 0 || [value integerValue] >= 200) {
return NO;
}
return YES;
}
@end
validateValue
方法的工作原理:
4.1. 先找一下你的类中是否实现了方法-(BOOL)validate<Key>:error:
4.2. 如果实现了就会根据实现方法里面的自定义逻辑返回NO
或者YES
,如果没有实现这个方法,则系统默认返回就是YES
五、KVC与字典
利用这两个API
/* Given an array of keys, return a dictionary containing the keyed attribute values, to-one-related objects, and/or collections of to-many-related objects. Entries for which -valueForKey: returns nil have NSNull as their value in the returned dictionary.
*/
- (NSDictionary<NSString *, id> *)dictionaryWithValuesForKeys:(NSArray<NSString *> *)keys;
/* Given a dictionary containing keyed attribute values, to-one-related objects, and/or collections of to-many-related objects, set the keyed values. Dictionary entries whose values are NSNull result in -setValue:nil forKey:key messages being sent to the receiver.
*/
- (void)setValuesForKeysWithDictionary:(NSDictionary<NSString *, id> *)keyedValues;
示例3
// KVC字典操作
- (void) dictionaryTest {
ZYPerson* p = [ZYPerson new];
NSDictionary* dict = @{
@"name":@"Tom",
@"age":@18,
@"nick":@"Cat",
@"height":@180
};
[p setValuesForKeysWithDictionary:dict];
NSLog(@"p.name = %@, p.age = %d, p.nick =%@, p.height = %f", p.name, p.age, p.nick, p.height);
NSArray* keys = @[@"name", @"age"];
NSDictionary* dict1 = [p dictionaryWithValuesForKeys:keys];
NSLog(@"%@", dict1);
}
@end
输出如下:
2019-01-16 21:09:31.140841+0800 KVC001[24763:1926331] p.name = Tom, p.age = 18, p.nick =Cat, p.height = 180.000000
2019-01-16 21:09:31.141081+0800 KVC001[24763:1926331] {
age = 18;
name = Tom;
}
六、KVC的消息传递
1.length 字符串长度
2.lowercaseString 全部小写
3.uppercaseString 全部大写
4.capitalizedString 首字母大写
示例4
// KVC消息传递 array
- (void) arrayKVCTest {
NSArray* arr = @[@"monDay", @"tuesDay", @"wednesDay"];
NSArray* lengthArr = [arr valueForKey:@"length"];
NSLog(@"%@", lengthArr);
NSArray* lowercaseArr = [arr valueForKey:@"lowercaseString"];
NSLog(@"%@", lowercaseArr);
NSArray* uppercaseArr = [arr valueForKey:@"uppercaseString"];
NSLog(@"%@", uppercaseArr);
NSArray* capitalizedArr = [arr valueForKey:@"capitalizedString"];
NSLog(@"%@", capitalizedArr);
}
@end
结果如下:
2019-01-16 21:44:56.079415+0800 KVC001[25267:2056897] (
6,
7,
9
)
2019-01-16 21:44:56.079665+0800 KVC001[25267:2056897] (
monday,
tuesday,
wednesday
)
2019-01-16 21:44:56.079798+0800 KVC001[25267:2056897] (
MONDAY,
TUESDAY,
WEDNESDAY
)
2019-01-16 21:44:56.079937+0800 KVC001[25267:2056897] (
Monday,
Tuesday,
Wednesday
)
七、KVC容器操作
7.1聚合操作符
1.@avg 平均值
2.@count 数量
3.@max 最大值
4.@min 最小值
5.@sum 总和
- (void) contrainerTest {
NSMutableArray* students = [NSMutableArray array];
for (int i = 0; i < 6; i++) {
TZPerson* student = [TZPerson new];
NSDictionary* dict = @{
@"name":@"Tom",
@"age":@(18+i),
@"nick":@"Cat",
@"height":@(1.65 + 0.02*arc4random_uniform(6)),
};
[student setValuesForKeysWithDictionary:dict];
[students addObject:student];
}
NSLog(@"%@", [students valueForKey:@"height"]);
// 平均身高
float avg = [[students valueForKeyPath:@"@avg.height"] floatValue];
NSLog(@"%f", avg);
}
结果如下:
2019-01-16 22:05:20.043606+0800 KVC001[25551:2194029] (
"1.75",
"1.65",
"1.75",
"1.65",
"1.65",
"1.65"
)
2019-01-16 22:05:20.045527+0800 KVC001[25551:2194029] 1.683333
操作符取值格式如下:
7.2 数组操作符
1.@distinctUnionOfObjects 去重
2.@unionOfObjects 不去重
- (void) contrainerArrayTest {
NSMutableArray* students = [NSMutableArray array];
for (int i = 0; i < 6; i++) {
ZYPerson* student = [ZYPerson new];
NSDictionary* dict = @{
@"name":@"Tom",
@"age":@(18+i),
@"nick":@"Cat",
@"height":@(1.65 + 0.02*arc4random_uniform(6)),
};
[student setValuesForKeysWithDictionary:dict];
[students addObject:student];
}
NSLog(@"%@", [students valueForKey:@"height"]);
NSArray* arr = [students valueForKeyPath:@"@distinctUnionOfObjects.height"];
NSLog(@"arr = %@", arr);
NSArray* arr1 = [students valueForKeyPath:@"@unionOfObjects.height"];
NSLog(@"arr1 = %@", arr1);
}
结果如下:
2019-01-16 22:12:13.543600+0800 KVC001[25671:2242372] (
"1.73",
"1.71",
"1.67",
"1.65",
"1.65",
"1.73"
)
2019-01-16 22:12:13.543815+0800 KVC001[25671:2242372] arr = (
"1.65",
"1.73",
"1.71",
"1.67"
)
2019-01-16 22:12:13.543964+0800 KVC001[25671:2242372] arr1 = (
"1.73",
"1.71",
"1.67",
"1.65",
"1.65",
"1.73"
)
7.3嵌套集合(array&set)操作
1.@distinctUnionOfArrays 数组去重
2.@unionOfArrays 数组不去重
3.@distinctUnionOfSets 集合去重
- (void) containerNestingTest {
NSMutableArray* students = [NSMutableArray array];
for (int i = 0; i < 6; i++) {
TZPerson* student = [TZPerson new];
NSDictionary* dict = @{
@"name":@"Tom",
@"age":@(18+i),
@"nick":@"Cat",
@"height":@(1.65 + 0.02*arc4random_uniform(6)),
};
[student setValuesForKeysWithDictionary:dict];
[students addObject:student];
}
NSMutableArray* students1 = [NSMutableArray array];
for (int i = 0; i < 6; i++) {
TZPerson* student = [TZPerson new];
NSDictionary* dict = @{
@"name":@"Tom",
@"age":@(18+i),
@"nick":@"Cat",
@"height":@(1.65 + 0.02*arc4random_uniform(6)),
};
[student setValuesForKeysWithDictionary:dict];
[students1 addObject:student];
}
// 嵌套数组
NSArray* nestArr = @[students, students1];
NSArray* arr = [nestArr valueForKeyPath:@"@distinctUnionOfArrays.height"];
NSLog(@"arr = %@", arr);
NSArray* arr1 = [nestArr valueForKeyPath:@"@unionOfArrays.height"];
NSLog(@"arr1 = %@", arr1);
}
- (void) containerNestingTest1 {
NSMutableSet* students = [NSMutableSet set];
for (int i = 0; i < 6; i++) {
TZPerson* student = [TZPerson new];
NSDictionary* dict = @{
@"name":@"Tom",
@"age":@(18+i),
@"nick":@"Cat",
@"height":@(1.65 + 0.02*arc4random_uniform(6)),
};
[student setValuesForKeysWithDictionary:dict];
[students addObject:student];
}
NSLog(@"students = %@", [students valueForKey:@"height"]);
NSMutableSet* students1 = [NSMutableSet set];
for (int i = 0; i < 6; i++) {
TZPerson* student = [TZPerson new];
NSDictionary* dict = @{
@"name":@"Tom",
@"age":@(18+i),
@"nick":@"Cat",
@"height":@(1.65 + 0.02*arc4random_uniform(6)),
};
[student setValuesForKeysWithDictionary:dict];
[students1 addObject:student];
}
NSLog(@"students1 = %@", [students1 valueForKey:@"height"]);
NSSet* nestSet = [NSSet setWithObjects:students, students1, nil];
NSArray* arr1 = [nestSet valueForKeyPath:@"@distinctUnionOfSets.height"];
NSLog(@"arr1 = %@", arr1);
}
八、KVC集合代理对象
通过一个key
映射多个方法
// TZPerson.h
@interface TZPerson : NSObject
@property (nonatomic, assign) NSUInteger count;
@property (nonatomic, strong) NSMutableArray *penArr;
@end
// TZPerson.m
@implementation TZPerson
- (NSUInteger) countOfBooks {
return self.count;
}
- (id) objectInBooksAtIndex:(NSUInteger)index {
return [NSString stringWithFormat:@"book %lu", index];
}
// 个数
- (NSUInteger) countOfPens {
return [self.penArr count];
}
// 是否包含这个成员对象
- (id) memberOfPens:(id)object {
return [self.penArr containsObject:object] ? object : nil;
}
// 迭代器
- (id) enumeratorOfPens {
return [self.penArr objectEnumerator];
}
@end
// ViewController.h
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
TZPerson* p = [TZPerson new];
p.count = 5;
NSLog(@"books = %@", [p valueForKey:@"books"]);
p.penArr = [NSMutableArray arrayWithObjects:@"pen0", @"pen1", @"pen2", @"pen3", nil];
NSSet* set = [p valueForKey:@"pens"];
NSLog(@"pens = %@", set);
}
结果如下
2019-01-17 15:04:27.768932+0800 KVC001[4017:619765] books = (
"book 0",
"book 1",
"book 2",
"book 3",
"book 4"
)
2019-01-17 15:04:27.769168+0800 KVC001[4017:619765] pens = {(
pen0,
pen1,
pen2,
pen3
)}