编译器会自动生成setter和getter的访问器方法,但是这样是直接调用访问器方法,还有一种以间接的方法进行修改对象的属性的方法,这种方法使用的就是键值编码。
// 这样的就是访问 访问方法选择器的方法,直接使用setter方法进行方法的赋值
[self.view setBackgroundColor:[UIColor redColor]];
// 这样就是使用键值方法,对对象的某个属性进行间接赋值的方法
[self.view setValue:[UIColor redColor] forKey:@"backgroundColor"];
//forKeyPath是关键路径,前面的属性是相对于接受者的,下一级的属性是相对于前面的属性的,这个例子view是相对于self的,backgroundView是相对于view的
[self setValue:[UIColor redColor] forKeyPath:@"view.backgroundColor"];
// 获取制定key的值
id value = [self.view valueForKey:@"backgroundColor"];
/**
如果valueforkey的key不存在,会调用这个方法,但是必须得在子类中,进行重写,如果在其他类中实现,不会调用这个方法,否则会奔溃
例如上面的方法,必须在self.view方法中的view中进行重写这个方法
@param key 制定的key值
@return 返回的值
*/
-(id)valueForUndefinedKey:(NSString *)key{
return @"";
}
// 返回的值是个字典,参数为为字符串的数组,调用这个方法会对参数的数组中key值进行valueForKey操作,返回的字典就是一个key和keyvalue的值,组成的字典
NSDictionary * dict = [self dictionaryWithValuesForKeys:@[@"view",@"viewIfLoaded",@"parentViewController",@"presentedViewController",@"presentingViewController"]];
// setter方法
//给消息接受者指定的key设置指定的值
[self.view setValue:[UIColor yellowColor] forKey:@"backgroundColor"];
[self setValue:[UIColor orangeColor] forKeyPath:@"view.backgroundColor"];
//setValuesForKeysWithDictionary底层是对字典中的key进行遍历,对每个key调用setValue:forKeyPath:方法,在字典中的key不能使用关键路径的形式
[self.view setValuesForKeysWithDictionary:@{@"backgroundColor":[UIColor purpleColor],@"alpha":[NSNumber numberWithFloat:0.5]}];
@property (nonatomic,assign) int num;
// 当给非对象的属性key进行辅助nil对象的时候,会调用setNilValueForKey方法,可以在里面进行修改设置
[self setValue:nil forKeyPath:@"num"];
//对非对象的属性进行赋值为nil的时候会调用
-(void)setNilValueForKey:(NSString *)key{
if ([key isEqualToString:@"num"]) {
_num = 1;
}
}
访问集合对象的属性的方法
@property (nonatomic,strong) NSArray * numArray;
self.numArray = @[@"1",@"2",@"3",@"4",@"10"];
NSMutableArray * array = [self mutableArrayValueForKey:@"numArray"];
使用集合运算符进行操作
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface Person : NSObject
@property (nonatomic,copy)NSString * name;
@property (nonatomic,strong) NSNumber * age;
@property (nonatomic,strong) NSDate * date;
@end
// 使用关键路径的getter方法时候,在关键路径中嵌入集合运算符例如在集合运算符前加上@标志,在集合运算符前面的部分为左键,代表的是消息的接受者的操作集合,集合运算符后面的部分,成为右键,表示的是指定集合操作符处理集合里面的指定的属性
NSNumber * string = [self valueForKeyPath:@"numArray.@sum.floatValue"];
NSNumber * string1 = [self.numArray valueForKeyPath:@"@sum.floatValue"];
Person * p = [[Person alloc]init];
self.persongArray = [NSMutableArray array];
p.age = @20;
p.name = @"小明";
[self.persongArray addObject:p];
Person * p1 = [[Person alloc]init];
p1.age = @22;
p1.name = @"小王";
[self.persongArray addObject:p1];
// 求和操作
id sumAge = [self valueForKeyPath:@"persongArray.@sum.age"];
// 求平均值操作
NSNumber * avgAge = [self valueForKeyPath:@"persongArray.@avg.age"];
// 计算操作没有左键的方式,因为接受者就是个集合了
NSNumber * count = [self.persongArray valueForKeyPath:@"@count"];
// 计算个数的操作符,还有左键的方式,左键代表的消息接受者的操作集合对象
NSNumber * count1 = [self valueForKeyPath:@"persongArray.@count"];
// 取集合中的最大值
NSNumber * maxAge = [self valueForKeyPath:@"persongArray.@max.age"];
// 取集合中的最小值
NSNumber * minAge = [self valueForKeyPath:@"persongArray.@min.age"];
//获取一个数组集合,去掉重复内容的数组
NSArray * distinctAgeArray = [self valueForKeyPath:@"persongArray.@distinctUnionOfObjects.age"];
//获取一个数组集合,没有去掉重复内容的数组
NSArray * ageArray = [self valueForKeyPath:@"persongArray.@unionOfObjects.age"];
数组嵌套数组的操作
@property (nonatomic,strong) NSMutableArray * arrayOfArray;
@property (nonatomic,strong) NSMutableArray * myArray;
self.arrayOfArray = [NSMutableArray array];
[self.arrayOfArray addObject:self.persongArray];
Person * p3 = [[Person alloc]init];
p3.age = @23;
p3.name = @"小许";
self.myArray = [NSMutableArray array];
[self.myArray addObject:p3];
[self.arrayOfArray addObject:self.myArray];
// 从嵌套的数组中,取出右键指定的属性,组成一个新的数组,这个不会去掉重复的内容
NSArray * arrayOfArray = [self valueForKeyPath:@"arrayOfArray.@unionOfArrays.age"];
// 从嵌套的数组中,取出右键指定的属性,组成一个新的数组,这个会去掉重复的内容
NSArray * distinctArrayOfArray = [self valueForKeyPath:@"arrayOfArray.@distinctUnionOfArrays.age"];
@distinctUnionOfSets
指定@distinctUnionOfSets运算符时,valueForKeyPath:创建并返回一个NSSet对象,该对象包含与右键路径指定的属性对应的所有集合的组合的不同对象。
此运算符的行为与此类似@distinctUnionOfArrays,只是它需要一个NSSet包含NSSet对象实例而不是NSArray实例实例的NSArray实例。此外,它返回一个NSSet实例。假设示例数据已存储在集合而不是数组中,示例调用和结果与显示的相同@distinctUnionOfArrays。
表示非对象值
//创建一个结构体
struct ThreeFloat {
float a,b,c;
};
//增加一个属性
@property (nonatomic,assign) struct ThreeFloat threeFloat;
// 给结构体赋值
struct ThreeFloat threeFloat = {1.,2.,4.};
// 转为nsvalue对象
NSValue * threeValue = [NSValue value:&threeFloat withObjCType:@encode(struct ThreeFloat)];
// 使用kvc进行赋值操作
[self setValue:threeValue forKey:@"threeFloat"];
验证属性
NSError * error;
BOOL isBool = [self validateValue:&threeValue forKey:@"threeFloat" error:&error];
如果当其中一个键值编码的访问器方法无法找到属性的访问器时候,可以看这个类是否可以使用实例变量的方式
BOOL isbool = [[self class] accessInstanceVariablesDirectly];
使用键值编码协议当中的方法来获取集合属性的操作
@property (nonatomic,strong) NSMutableArray * nameArray;
// 初始化一个数组
self.nameArray = [NSMutableArray arrayWithArray:@[@"小明",@"小李",@"小王"]];
// 调用数组的个数
NSInteger nameCount = [self countOfNameArray];
//调用指定索引的值
NSString * nameString = [self objectInNameArrayAtIndex:0];
// 返回一个数组,数组值为指定多个多个索引值的对象
NSArray * tempNameArray = [self nameArrayAtIndexes:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, 2)]];
// 这里对指针的修饰符进行了更改,这是不安全的不增加引用计数的
__unsafe_unretained NSArray * tempArray = [NSArray array];
// 获取指定数组中的索引的对象添加到指定的数组中
[self getNameArray:&tempArray range:NSMakeRange(0, 1)];
// 进行插入操作
[self insertNameArray:@[@"1234"] atIndexes:[NSIndexSet indexSetWithIndex:0]];
[self insertObject:@[@"567",@"789"] inNameArrayAtIndex:0];
// 进行删除操作
[self removeNameArrayObject:@"1234"];
[self removeObjectFromNameArrayAtIndex:0];
[self removeNameArrayAtIndexes:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, 2)]];
// 替换操作
[self replaceNameArrayAtIndexes:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, 2)] withNameArray:@[@"1234",@"5678"]];
[self replaceObjectInNameArrayAtIndex:3 withObject:@"0233"];
//获取数组的个数 countOf<Key>
-(NSUInteger)countOfNameArray{
return self.nameArray.count;
}
//获取指定索引的值 objectIn<Key>AtIndex:
-(id)objectInNameArrayAtIndex:(NSUInteger)index{
return [self.nameArray objectAtIndex:index];
}
//获取指定的多个索引的值,返回一个数组对象 <key>AtIndexes:
-(NSArray *)nameArrayAtIndexes:(NSIndexSet *)indexes{
return [self.nameArray objectsAtIndexes:indexes];
}
//获取指定数组中索引值的对象,放到指定的数组中 get<Key>:range:
-(void)getNameArray:(NSArray *__unsafe_unretained *)buffer range:(NSRange)inRange{
[self.nameArray getObjects:buffer range:inRange];
}
//进行插入操作 insert<Key>:atIndexes:
-(void)insertNameArray:(NSArray *)array atIndexes:(NSIndexSet *)indexes{
[self.nameArray insertObjects:array atIndexes:indexes];
}
//插入操作 insertObject:in<Key>AtIndex:
-(void)insertObject:(NSArray *)object inNameArrayAtIndex:(NSUInteger)index{
[self.nameArray insertObject:object atIndex:index];
}
/删除指定的对象
-(void)removeNameArrayObject:(NSString *)object{
[self.nameArray removeObject:object];
}
//删除指定的索引的对象
-(void)removeObjectFromNameArrayAtIndex:(NSUInteger)index{
[self.nameArray removeObjectAtIndex:index];
}
//删除指定的索引的集合对象
-(void)removeNameArrayAtIndexes:(NSIndexSet *)indexes{
[self.nameArray removeObjectsAtIndexes:indexes];
}
//替换多个对象 replace<Key>AtIndexes:with<Key>:
-(void)replaceNameArrayAtIndexes:(NSIndexSet *)indexes withNameArray:(NSArray *)array{
[self.nameArray replaceObjectsAtIndexes:indexes withObjects:array];
}
//替换一个对象 replaceObjectIn<Key>AtIndex:withObject:
-(void)replaceObjectInNameArrayAtIndex:(NSUInteger)index withObject:(id)object{
[self.nameArray replaceObjectAtIndex:index withObject:object];
}
KVC底层原理分析
setValue:forKey:赋值原理如下:
1.去模型中查找有没有对应的setter方法:例如:setIcon方法,有就直接调用这个setter方法给模型这个属性赋值[self setIcon:dic[@"icon"]];
2.如果找不到setter方法,接着就会去寻找有没有icon属性,如果有,就直接访问模型中的icon属性,进行赋值,icon=dict[@"icon"];
3.如果找不到icon属性,接着又会去寻找_icon属性,如果有,直接进行赋值_icon=dict[@"icon"];
4.如果都找不到就会报错:[<Flag 0X7fb74bc7a2c0> setValue:forUndefinedKey:]
如果对某个类,不允许使用KVC,可以通过设置 accessInstanceVariablesDirectly 控制。