一、简介
KVC是KeyValue Coding的简称,它是对NSObject的扩展NSKeyValueCoding,我们可以通过字符串的名字(key)来获取和设置属性的机制
二、普通对象&不可变容器
下面介绍一下几个方法,这几个方法的调用过程在 官方文档 有详细的说明。XCode(8.3)的 NSKeyValueCoding.h 头文件的注释中也有说明,我下边绘制了几张流程图
1. valueForKey:
为了使流程图更加清晰,这里没有列出全部的方法,有兴趣,可以查阅下文档,里边说的很详细,最好对照文档看,以便查看没有列出的方法以及详细的调用逻辑
2. setValue: forKey:
三、可变容器
1. mutableArrayValueForKey:
1.调用方法与调用 -valueForKey: 方法值的查找过程其实是类似的,但是它返回了一个集合代理对象,我们调用集合代理对象的insert或romove等方法就会触发如下图所示的处理过程。
2.这里并没有列出全部的插入,移除方法。文档里也详细说明实现至少一个插入方法和至少一个移除方法(文档里的意思应该是对应的插入移除方法要成对出现),调用 mutableArrayValueForKey: 才会返回集合代理对象
3.此外还有 mutableOrderedSetValueForKey: 与 mutableSetValueForKey: 两个方法,它们分别对有序可变集合与无序可变集合的操作,调用过程与 mutableArrayValueForKey: ;类似,不再详述
测试样例:
// KYSKVCMutableArrayTest.h
@interface KYSKVCMutableArrayTest : NSObject
@property (strong ,nonatomic) NSMutableArray<NSString *> *kys;
- (void)insertObject:(id)object inKysAtIndex:(NSUInteger)index;
- (void)removeObjectFromKysAtIndex:(NSUInteger)index;
-(void)addObjectObserverWithObject:(NSString *)str;
-(void)removeLastObjectObserver;
@end
// KYSKVCMutableArrayTest.m
@implementation KYSKVCMutableArrayTest
-(id)init{
if (self == [super init]){
_kys = [NSMutableArray new];
[self addObserver:self forKeyPath:@"kys" options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld context:nil];
}
return self;
}
-(void)dealloc{
[self removeObserver:self forKeyPath:@"kys"];
}
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context{
NSLog(@"收到数组变化通知:%@",change);
}
- (void)insertObject:(id)object inKysAtIndex:(NSUInteger)index {
NSLog(@"insertObject:inKysAtIndex:%@",@(index));
[self.kys insertObject:object atIndex:index];
}
- (void)removeObjectFromKysAtIndex:(NSUInteger)index {
NSLog(@"removeObjectFromKysAtIndex:%@",@(index));
[self.kys removeObjectAtIndex:index];
}
-(void)addObjectObserverWithObject:(NSString *)str{
[[self mutableArrayValueForKey:@"kys"] addObject:str];
}
-(void)removeLastObjectObserver{
[[self mutableArrayValueForKey:@"kys"] removeLastObject];
}
@end
测试
KYSKVCMutableArrayTest *test = [[KYSKVCMutableArrayTest alloc] init];
NSMutableArray *mArray = [test mutableArrayValueForKey:@"kys"];
[mArray insertObject:@"000" atIndex:0];
[mArray removeObjectAtIndex:0];
打印结果
利用这个特性我们可以实现对数组变化的监听(KVO)
KYSKVCMutableArrayTest *test = [[KYSKVCMutableArrayTest alloc] init];
[test addObjectObserverWithObject:@"111"];
[test removeLastObjectObserver];
打印结果
四、其他方法
- dictionaryWithValuesForKeys: 传入一组key,返回这组key与value的字典
- setValuesForKeysWithDictionary:可以通过字典设置一组值
//这里用字典进行测试,使用一个自定义的模型类也是一样的
NSMutableDictionary *mDic=[@{@"id":@"001",
@"name":@"kys",
@"des":@"adhkaslkfdladf"} mutableCopy];
NSLog(@"%@",[mDic dictionaryWithValuesForKeys:@[@"id",@"name"]]);
[mDic setValuesForKeysWithDictionary:@{@"id":@"002",@"name":@"kys2"}];
NSLog(@"%@",mDic);
打印结果
- valueForKeyPath:其实就是先用“.”把各个key提取出来依次查找
- setValue: forKeyPath: 先用“.”把各个key提取出来,查找到最后一层进行赋值
五、检查正确性
我们可以通过以下两个方法验证设置的值是否正确,如果不正确可以在这两个方法里做出相应的处理
- validateValue: forKey: error:
- validateValue: forKeyPath: error:
六、集合操作
这些操作其实是很方便的 官方文档 有相关的样例
1. 常规操作符
- @sum:值的总和
- @avg:平均值
- @count:总个数
- @max:最大值
- @min:最小值
NOTE:由于是KeyPath可以这样调用@"@sum.floatValue"
KYSKVCTest *test1=[[KYSKVCTest alloc] init];
test1.num=1;
KYSKVCTest *test2=[[KYSKVCTest alloc] init];
test2.num=2;
KYSKVCTest *test3=[[KYSKVCTest alloc] init];
test3.num=3;
NSArray *array=@[test1,test2,test3];
NSNumber* sum = [array valueForKeyPath:@"@sum.num"];
NSNumber* avg = [array valueForKeyPath:@"@avg.num"];
NSNumber* count = [array valueForKeyPath:@"@count"];
NSNumber* min = [array valueForKeyPath:@"@min.num"];
NSNumber* max = [array valueForKeyPath:@"@max.num"];
NSLog(@"sum:%@, avg:%@, count:%@, min:%@, max:%@",sum,avg,count,min,max);
//打印结果:sum:6, avg:2, count:3, min:1, max:3
2. 对象操作
- @distinctUnionOfObjects:元素唯一,会进行去重
- @unionOfObjects:不会去重
KYSKVCTest *test1=[[KYSKVCTest alloc] init];
test1.num=1;
KYSKVCTest *test2=[[KYSKVCTest alloc] init];
test2.num=2;
KYSKVCTest *test3=[[KYSKVCTest alloc] init];
test3.num=3;
KYSKVCTest *test4=[[KYSKVCTest alloc] init];
test4.num=2;
NSArray *array=@[test1,test2,test3,test4];
NSArray *distinctUnionOfObjectsArray=[array valueForKeyPath:@"@distinctUnionOfObjects.num"];
NSLog(@"distinctUnionOfObjects:%@",distinctUnionOfObjectsArray);
NSArray *unionOfObjectsArray=[array valueForKeyPath:@"@unionOfObjects.num"];
NSLog(@"unionOfObjects:%@",unionOfObjectsArray);
/* 打印结果
distinctUnionOfObjects:(
3,
2,
1
)
unionOfObjects:(
1,
2,
3,
2
)
*/
3. 集合操作
- @distinctUnionOfArrays:去重
- @unionOfArrays:不去重
- @distinctUnionOfSets:集合无重复的
array与set类似,这里只给出array的样例
KYSKVCTest *test1=[[KYSKVCTest alloc] init];
test1.num=1;
KYSKVCTest *test2=[[KYSKVCTest alloc] init];
test2.num=2;
KYSKVCTest *test3=[[KYSKVCTest alloc] init];
test3.num=3;
KYSKVCTest *test4=[[KYSKVCTest alloc] init];
test4.num=2;
NSArray *array1=@[test1,test2,test3,test4];
KYSKVCTest *test5=[[KYSKVCTest alloc] init];
test5.num=5;
KYSKVCTest *test6=[[KYSKVCTest alloc] init];
test6.num=6;
KYSKVCTest *test7=[[KYSKVCTest alloc] init];
test7.num=6;
KYSKVCTest *test8=[[KYSKVCTest alloc] init];
test8.num=7;
KYSKVCTest *test9=[[KYSKVCTest alloc] init];
test9.num=9;
KYSKVCTest *test10=[[KYSKVCTest alloc] init];
test10.num=9;
KYSKVCTest *test11=[[KYSKVCTest alloc] init];
test11.num=10;
//注意这里的嵌套
NSArray *array2=@[test5,test6,test7,test8,@[test9,test10,test11]];
NSArray *array=@[array1,array2];
NSArray *distinctUnionOfArraysArray=[array valueForKeyPath:@"@distinctUnionOfArrays.num"];
NSLog(@"distinctUnionOfArrays:%@",distinctUnionOfArraysArray);
NSArray *unionOfArraysArray=[array valueForKeyPath:@"@unionOfArrays.num"];
NSLog(@"unionOfArrays:%@",unionOfArraysArray);
/* 打印结果
distinctUnionOfArrays:(
5,
1,
(
9,
9,
10
),
6,
2,
7,
3
)
unionOfArrays:(
1,
2,
3,
2,
5,
6,
6,
7,
(
9,
9,
10
)
)
*/
七、实现方式
运用isa-swizzling技术,通过isa-swizzling来实现其内部定位查找
八、总结
之前在SDWebImage的源码里,看见直接对数组调用valueForKey:方法,当时不明白什么意思,然后查阅了一下相关的文档,发现很多方法都很有用,可以使我们的代码更加精简。如果文章里哪里有不对的地方,欢迎指出_!