在讲KVC之前我们先来了解几个概念
iOS 成员变量,实例变量,属性的区别
- 成员变量
一个类里面所有的变量都是成员变量 - 实例变量
实例创建出来的变量,比如Class
id 不一定,id是特殊的Class - 属性
属性一般会有一个默认的setter+getter方法
我们知道苹果早期的编译器是GCC,后来变成了LLVM
经过LLVM,如果发现没有匹配到实例变量的属性的时候,就会自动创建一个_<key>
@interface Person : NSObject{
@public
NSString *myName; //成员
id hell; // id - > class
int age;
}
@property (nonatomic, copy) NSString *name;
KVC取值赋值原理
为什么要介绍上面三个东西,因为KVC就是在上面这三个基础上操作的
KVC的源码并没有开源,所以我们学习KVC的方法就是去查看苹果的官方文档
https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/KeyValueCoding/SearchImplementation.html#//apple_ref/doc/uid/20000955-CJBBBFFA
通过官方文档我们可以看出:
KVC是通过<key>间接访问成员变量的一种机制
通过键值编码
这里我简单翻译了一下关键的部分
Getter的搜索方式
-
搜索实例与像一个名字找到的第一个访问方法
get<Key>
,<key>
,is<Key>
,或者_<key>
,按照这个顺序。如果找到,则调用它并执行步骤5。否则,继续下一步。 -
如果未找到简单的访问器方法,请在实例中搜索名称与模式
countOf<Key>
和objectIn<Key>AtIndex:
(与NSArray
该类定义的原始方法<key>AtIndexes:
相对应)和(与该[NSArray](https://developer.apple.com/library/archive/documentation/LegacyTechnologies/WebObjects/WebObjects_3.5/Reference/Frameworks/ObjC/Foundation/Classes/NSArrayClassCluster/Description.html#//apple_ref/occ/cl/NSArray)
方法相对应)相匹配的方法objectsAtIndexes:
。如果找到其中的第一个以及其他两个中的至少一个,则创建一个响应所有
NSArray
方法的集合代理对象并将其返回。否则,请继续执行步骤3。代理对象随后将任何
NSArray
接收到的一些组合的消息countOf<Key>
,objectIn<Key>AtIndex:
和<key>AtIndexes:
消息给键-值编码创建它兼容的对象。如果原始对象还实现了名称为的可选方法get<Key>:range:
,则代理对象也会在适当时使用该方法。实际上,代理对象与与键值编码兼容的对象一起使用,可以使基础属性的行为就好像它是NSArray
,即使不是。 -
如果没有找到简单的访问方法或阵列访问方法组,寻找一个三重的方法命名
countOf<Key>
,enumeratorOf<Key>
和memberOf<Key>:
(对应于由所定义的原始的方法[NSSet](https://developer.apple.com/library/archive/documentation/LegacyTechnologies/WebObjects/WebObjects_3.5/Reference/Frameworks/ObjC/Foundation/Classes/NSSetClassCluster/Description.html#//apple_ref/occ/cl/NSSet)
类)。如果找到所有三个方法,请创建一个响应所有
NSSet
方法的集合代理对象并将其返回。否则,请继续执行步骤4。随后,此代理对象将
NSSet
收到的任何消息转换为,和消息的某种组合countOf<Key>
,以创建该对象的对象。实际上,代理对象与与键值编码兼容的对象一起使用,可以使基础属性的行为就好像它是,即使不是。enumeratorOf<Key>``memberOf<Key>:``NSSet
-
如果发现收集的访问方法没有简单的存取方法或者组,如果接收器的类方法
[accessInstanceVariablesDirectly]
返回YES
,搜索名为实例变量_<key>
,_is<Key>
,<key>
,或者is<Key>
,按照这个顺序。如果找到,请直接获取实例变量的值,然后继续执行步骤5。否则,请继续执行步骤6。 -
如果检索到的属性值是对象指针,则只需返回结果。
如果该值是所支持的标量类型
NSNumber
,则将其存储在NSNumber
实例中并返回它。如果结果是NSNumber不支持的标量类型,请转换为
NSValue
对象并返回该对象。 -
如果其他所有方法均失败,请调用
[valueForUndefinedKey:]
。默认情况下会引发异常,但是的子类NSObject
可能提供特定于键的行为。
Setter
setValue:forKey:
使用以下步骤
-
按此顺序查找第一个名为
set<Key>:
or_set<Key>
的访问器。如果找到,请使用输入值(或根据需要解包的值)调用它并完成。 -
如果没有实现上面两个
set<Key>:
or_set<Key>
,如果类方法accessInstanceVariablesDirectly
返回YES
(默认),寻找一个实例变量与名称类似_<key>
,_is<Key>
,<key>
,或者is<Key>
,按照这个顺序。如果找到,直接用输入值(或展开值)设置变量并完成操作。 -
在找不到访问器或实例变量时,调用
setValue:forUndefinedKey:
。默认情况下会引发异常,但是的子类NSObject
可能提供特定于键的行为。
如果你不了解这个流程,你就会很迷惑。
流程图:
image.pngKVC的延伸应用
KVC还有一些比较有意思的东西
#pragma mark - array取值
- (void)arrayDemo{
NSLog(@"******************array取值****************************");
LGPerson *p = [LGPerson new];
p.penArr = [NSMutableArray arrayWithObjects:@"pen0", @"pen1", @"pen2", @"pen3", nil];
NSArray *arr = [p valueForKey:@"pens"];
NSLog(@"pens = %@", arr);
//NSLog(@"%@",arr[0]);
NSLog(@"%d",[arr containsObject:@"pen1"]);
// 遍历
NSEnumerator *enumerator = [arr objectEnumerator];
NSString *str = nil;
while (str = [enumerator nextObject]) {
NSLog(@"%@", str);
}
}
#pragma mark - 字典操作
- (void)dictionaryTest{
NSLog(@"******************KVC字典转模型****************************");
NSDictionary* dict = @{
@"name":@"Fuye",
@"nick":@"KC",
@"subject":@"iOS",
@"age":@18,
@"length":@180
};
LGPerson *p = [[LGPerson alloc] init];
// 字典转模型
[p setValuesForKeysWithDictionary:dict];
NSLog(@"%@",p);
// 键数组转模型到字典
NSArray *array = @[@"name",@"age"];
NSDictionary *dic = [p dictionaryWithValuesForKeys:array];
NSLog(@"%@",dic);
}
#pragma mark - KVC消息传递
- (void)arrayMessagePass{
NSLog(@"******************KVC消息传递****************************");
NSArray *array = @[@"Rose",@"Fuye",@"Natasy",@"ChangE"];
NSArray *lenStr= [array valueForKeyPath:@"length"];
NSLog(@"%@",lenStr);// 消息从array传递给了string
NSArray *lowStr= [array valueForKeyPath:@"lowercaseString"];
NSLog(@"%@",lowStr);
}
#pragma mark - 聚合操作符
// @avg、@count、@max、@min、@sum
- (void)aggregationOperator{
NSLog(@"******************@avg、@count、@max、@min、@sum****************************");
NSMutableArray *personArray = [NSMutableArray array];
for (int i = 0; i < 6; i++) {
LGPerson *p = [LGPerson new];
NSDictionary* dict = @{
@"name":@"Tom",
@"age":@(18+i),
@"nick":@"Cat",
@"length":@(180 + 2*arc4random_uniform(6)),
};
[p setValuesForKeysWithDictionary:dict];
[personArray addObject:p];
}
NSLog(@"%@", [personArray valueForKey:@"length"]);
/// 平均身高
float avg = [[personArray valueForKeyPath:@"@avg.length"] floatValue];
NSLog(@"%f", avg);
int count = [[personArray valueForKeyPath:@"@count.length"] intValue];
NSLog(@"%d", count);
int sum = [[personArray valueForKeyPath:@"@sum.length"] intValue];
NSLog(@"%d", sum);
int max = [[personArray valueForKeyPath:@"@max.length"] intValue];
NSLog(@"%d", max);
int min = [[personArray valueForKeyPath:@"@min.length"] intValue];
NSLog(@"%d", min);
}
// 数组操作符 @distinctUnionOfObjects @unionOfObjects
- (void)arrayOperator{
NSLog(@"****************** @distinctUnionOfObjects @unionOfObjects****************************");
NSMutableArray *personArray = [NSMutableArray array];
for (int i = 0; i < 6; i++) {
LGPerson *p = [LGPerson new];
NSDictionary* dict = @{
@"name":@"Tom",
@"age":@(18+i),
@"nick":@"Cat",
@"length":@(175 + 2*arc4random_uniform(6)),
};
[p setValuesForKeysWithDictionary:dict];
[personArray addObject:p];
}
NSLog(@"%@", [personArray valueForKey:@"length"]);
// 返回操作对象指定属性的集合
NSArray* arr1 = [personArray valueForKeyPath:@"@unionOfObjects.length"];
NSLog(@"arr1 = %@", arr1);
// 返回操作对象指定属性的集合 -- 去重
NSArray* arr2 = [personArray valueForKeyPath:@"@distinctUnionOfObjects.length"];
NSLog(@"arr2 = %@", arr2);
}
// 嵌套集合(array&set)操作 @distinctUnionOfArrays @unionOfArrays @distinctUnionOfSets
- (void)arrayNesting{
NSLog(@"******************@distinctUnionOfArrays @unionOfArrays @distinctUnionOfSets****************************");
NSMutableArray *personArray1 = [NSMutableArray array];
for (int i = 0; i < 6; i++) {
LGPerson *person = [LGPerson new];
NSDictionary* dict = @{
@"name":@"Tom",
@"age":@(18+i),
@"nick":@"Cat",
@"length":@(175 + 2*arc4random_uniform(6)),
};
[person setValuesForKeysWithDictionary:dict];
[personArray1 addObject:person];
}
NSMutableArray *personArray2 = [NSMutableArray array];
for (int i = 0; i < 6; i++) {
LGPerson *person = [LGPerson new];
NSDictionary* dict = @{
@"name":@"Tom",
@"age":@(18+i),
@"nick":@"Cat",
@"length":@(175 + 2*arc4random_uniform(6)),
};
[person setValuesForKeysWithDictionary:dict];
[personArray2 addObject:person];
}
// 嵌套数组
NSArray* nestArr = @[personArray1, personArray2];
NSArray* arr = [nestArr valueForKeyPath:@"@distinctUnionOfArrays.length"];
NSLog(@"arr = %@", arr);
NSArray* arr1 = [nestArr valueForKeyPath:@"@unionOfArrays.length"];
NSLog(@"arr1 = %@", arr1);
}
- (void)setNesting{
NSLog(@"******************setNesting****************************");
NSMutableSet *personSet1 = [NSMutableSet set];
for (int i = 0; i < 6; i++) {
LGPerson *person = [LGPerson new];
NSDictionary* dict = @{
@"name":@"Tom",
@"age":@(18+i),
@"nick":@"Cat",
@"length":@(175 + 2*arc4random_uniform(6)),
};
[person setValuesForKeysWithDictionary:dict];
[personSet1 addObject:person];
}
NSLog(@"personSet1 = %@", [personSet1 valueForKey:@"length"]);
NSMutableSet *personSet2 = [NSMutableSet set];
for (int i = 0; i < 6; i++) {
LGPerson *person = [LGPerson new];
NSDictionary* dict = @{
@"name":@"Tom",
@"age":@(18+i),
@"nick":@"Cat",
@"length":@(175 + 2*arc4random_uniform(6)),
};
[person setValuesForKeysWithDictionary:dict];
[personSet2 addObject:person];
}
NSLog(@"personSet2 = %@", [personSet2 valueForKey:@"length"]);
// 嵌套set
NSSet* nestSet = [NSSet setWithObjects:personSet1, personSet2, nil];
// 交集
NSArray* arr1 = [nestSet valueForKeyPath:@"@distinctUnionOfSets.length"];
NSLog(@"arr1 = %@", arr1);
}
如果不阅读官方文档,很难想象KVC还有这么有意思的东西可以玩一玩,可以通过KVC做很多事,所以对于底层的探索是每一个开发者都应该去做的。
网友评论