編譯器會(huì)自動(dòng)生成setter和getter的訪問(wèn)器方法,但是這樣是直接調(diào)用訪問(wèn)器方法,還有一種以間接的方法進(jìn)行修改對(duì)象的屬性的方法,這種方法使用的就是鍵值編碼。
// 這樣的就是訪問(wèn) 訪問(wèn)方法選擇器的方法,直接使用setter方法進(jìn)行方法的賦值
[self.view setBackgroundColor:[UIColor redColor]];
// 這樣就是使用鍵值方法,對(duì)對(duì)象的某個(gè)屬性進(jìn)行間接賦值的方法
[self.view setValue:[UIColor redColor] forKey:@"backgroundColor"];
//forKeyPath是關(guān)鍵路徑,前面的屬性是相對(duì)于接受者的,下一級(jí)的屬性是相對(duì)于前面的屬性的,這個(gè)例子view是相對(duì)于self的,backgroundView是相對(duì)于view的
[self setValue:[UIColor redColor] forKeyPath:@"view.backgroundColor"];
// 獲取制定key的值
id value = [self.view valueForKey:@"backgroundColor"];
/**
如果valueforkey的key不存在,會(huì)調(diào)用這個(gè)方法,但是必須得在子類中,進(jìn)行重寫,如果在其他類中實(shí)現(xiàn),不會(huì)調(diào)用這個(gè)方法,否則會(huì)奔潰
例如上面的方法,必須在self.view方法中的view中進(jìn)行重寫這個(gè)方法
@param key 制定的key值
@return 返回的值
*/
-(id)valueForUndefinedKey:(NSString *)key{
return @"";
}
// 返回的值是個(gè)字典,參數(shù)為為字符串的數(shù)組,調(diào)用這個(gè)方法會(huì)對(duì)參數(shù)的數(shù)組中key值進(jìn)行valueForKey操作,返回的字典就是一個(gè)key和keyvalue的值,組成的字典
NSDictionary * dict = [self dictionaryWithValuesForKeys:@[@"view",@"viewIfLoaded",@"parentViewController",@"presentedViewController",@"presentingViewController"]];
// setter方法
//給消息接受者指定的key設(shè)置指定的值
[self.view setValue:[UIColor yellowColor] forKey:@"backgroundColor"];
[self setValue:[UIColor orangeColor] forKeyPath:@"view.backgroundColor"];
//setValuesForKeysWithDictionary底層是對(duì)字典中的key進(jìn)行遍歷,對(duì)每個(gè)key調(diào)用setValue:forKeyPath:方法,在字典中的key不能使用關(guān)鍵路徑的形式
[self.view setValuesForKeysWithDictionary:@{@"backgroundColor":[UIColor purpleColor],@"alpha":[NSNumber numberWithFloat:0.5]}];
@property (nonatomic,assign) int num;
// 當(dāng)給非對(duì)象的屬性key進(jìn)行輔助nil對(duì)象的時(shí)候,會(huì)調(diào)用setNilValueForKey方法,可以在里面進(jìn)行修改設(shè)置
[self setValue:nil forKeyPath:@"num"];
//對(duì)非對(duì)象的屬性進(jìn)行賦值為nil的時(shí)候會(huì)調(diào)用
-(void)setNilValueForKey:(NSString *)key{
if ([key isEqualToString:@"num"]) {
_num = 1;
}
}
訪問(wèn)集合對(duì)象的屬性的方法
@property (nonatomic,strong) NSArray * numArray;
self.numArray = @[@"1",@"2",@"3",@"4",@"10"];
NSMutableArray * array = [self mutableArrayValueForKey:@"numArray"];

使用集合運(yùn)算符進(jìn)行操作
#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
// 使用關(guān)鍵路徑的getter方法時(shí)候,在關(guān)鍵路徑中嵌入集合運(yùn)算符例如在集合運(yùn)算符前加上@標(biāo)志,在集合運(yùn)算符前面的部分為左鍵,代表的是消息的接受者的操作集合,集合運(yùn)算符后面的部分,成為右鍵,表示的是指定集合操作符處理集合里面的指定的屬性
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"];
// 計(jì)算操作沒(méi)有左鍵的方式,因?yàn)榻邮苷呔褪莻€(gè)集合了
NSNumber * count = [self.persongArray valueForKeyPath:@"@count"];
// 計(jì)算個(gè)數(shù)的操作符,還有左鍵的方式,左鍵代表的消息接受者的操作集合對(duì)象
NSNumber * count1 = [self valueForKeyPath:@"persongArray.@count"];
// 取集合中的最大值
NSNumber * maxAge = [self valueForKeyPath:@"persongArray.@max.age"];
// 取集合中的最小值
NSNumber * minAge = [self valueForKeyPath:@"persongArray.@min.age"];
//獲取一個(gè)數(shù)組集合,去掉重復(fù)內(nèi)容的數(shù)組
NSArray * distinctAgeArray = [self valueForKeyPath:@"persongArray.@distinctUnionOfObjects.age"];
//獲取一個(gè)數(shù)組集合,沒(méi)有去掉重復(fù)內(nèi)容的數(shù)組
NSArray * ageArray = [self valueForKeyPath:@"persongArray.@unionOfObjects.age"];
數(shù)組嵌套數(shù)組的操作
@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];
// 從嵌套的數(shù)組中,取出右鍵指定的屬性,組成一個(gè)新的數(shù)組,這個(gè)不會(huì)去掉重復(fù)的內(nèi)容
NSArray * arrayOfArray = [self valueForKeyPath:@"arrayOfArray.@unionOfArrays.age"];
// 從嵌套的數(shù)組中,取出右鍵指定的屬性,組成一個(gè)新的數(shù)組,這個(gè)會(huì)去掉重復(fù)的內(nèi)容
NSArray * distinctArrayOfArray = [self valueForKeyPath:@"arrayOfArray.@distinctUnionOfArrays.age"];
@distinctUnionOfSets
指定@distinctUnionOfSets運(yùn)算符時(shí),valueForKeyPath:創(chuàng)建并返回一個(gè)NSSet對(duì)象,該對(duì)象包含與右鍵路徑指定的屬性對(duì)應(yīng)的所有集合的組合的不同對(duì)象。
此運(yùn)算符的行為與此類似@distinctUnionOfArrays,只是它需要一個(gè)NSSet包含NSSet對(duì)象實(shí)例而不是NSArray實(shí)例實(shí)例的NSArray實(shí)例。此外,它返回一個(gè)NSSet實(shí)例。假設(shè)示例數(shù)據(jù)已存儲(chǔ)在集合而不是數(shù)組中,示例調(diào)用和結(jié)果與顯示的相同@distinctUnionOfArrays。
表示非對(duì)象值
//創(chuàng)建一個(gè)結(jié)構(gòu)體
struct ThreeFloat {
float a,b,c;
};
//增加一個(gè)屬性
@property (nonatomic,assign) struct ThreeFloat threeFloat;
// 給結(jié)構(gòu)體賦值
struct ThreeFloat threeFloat = {1.,2.,4.};
// 轉(zhuǎn)為nsvalue對(duì)象
NSValue * threeValue = [NSValue value:&threeFloat withObjCType:@encode(struct ThreeFloat)];
// 使用kvc進(jìn)行賦值操作
[self setValue:threeValue forKey:@"threeFloat"];
驗(yàn)證屬性
NSError * error;
BOOL isBool = [self validateValue:&threeValue forKey:@"threeFloat" error:&error];
如果當(dāng)其中一個(gè)鍵值編碼的訪問(wèn)器方法無(wú)法找到屬性的訪問(wèn)器時(shí)候,可以看這個(gè)類是否可以使用實(shí)例變量的方式
BOOL isbool = [[self class] accessInstanceVariablesDirectly];
使用鍵值編碼協(xié)議當(dāng)中的方法來(lái)獲取集合屬性的操作
@property (nonatomic,strong) NSMutableArray * nameArray;
// 初始化一個(gè)數(shù)組
self.nameArray = [NSMutableArray arrayWithArray:@[@"小明",@"小李",@"小王"]];
// 調(diào)用數(shù)組的個(gè)數(shù)
NSInteger nameCount = [self countOfNameArray];
//調(diào)用指定索引的值
NSString * nameString = [self objectInNameArrayAtIndex:0];
// 返回一個(gè)數(shù)組,數(shù)組值為指定多個(gè)多個(gè)索引值的對(duì)象
NSArray * tempNameArray = [self nameArrayAtIndexes:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, 2)]];
// 這里對(duì)指針的修飾符進(jìn)行了更改,這是不安全的不增加引用計(jì)數(shù)的
__unsafe_unretained NSArray * tempArray = [NSArray array];
// 獲取指定數(shù)組中的索引的對(duì)象添加到指定的數(shù)組中
[self getNameArray:&tempArray range:NSMakeRange(0, 1)];
// 進(jìn)行插入操作
[self insertNameArray:@[@"1234"] atIndexes:[NSIndexSet indexSetWithIndex:0]];
[self insertObject:@[@"567",@"789"] inNameArrayAtIndex:0];
// 進(jìn)行刪除操作
[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"];
//獲取數(shù)組的個(gè)數(shù) countOf<Key>
-(NSUInteger)countOfNameArray{
return self.nameArray.count;
}
//獲取指定索引的值 objectIn<Key>AtIndex:
-(id)objectInNameArrayAtIndex:(NSUInteger)index{
return [self.nameArray objectAtIndex:index];
}
//獲取指定的多個(gè)索引的值,返回一個(gè)數(shù)組對(duì)象 <key>AtIndexes:
-(NSArray *)nameArrayAtIndexes:(NSIndexSet *)indexes{
return [self.nameArray objectsAtIndexes:indexes];
}
//獲取指定數(shù)組中索引值的對(duì)象,放到指定的數(shù)組中 get<Key>:range:
-(void)getNameArray:(NSArray *__unsafe_unretained *)buffer range:(NSRange)inRange{
[self.nameArray getObjects:buffer range:inRange];
}
//進(jìn)行插入操作 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];
}
/刪除指定的對(duì)象
-(void)removeNameArrayObject:(NSString *)object{
[self.nameArray removeObject:object];
}
//刪除指定的索引的對(duì)象
-(void)removeObjectFromNameArrayAtIndex:(NSUInteger)index{
[self.nameArray removeObjectAtIndex:index];
}
//刪除指定的索引的集合對(duì)象
-(void)removeNameArrayAtIndexes:(NSIndexSet *)indexes{
[self.nameArray removeObjectsAtIndexes:indexes];
}
//替換多個(gè)對(duì)象 replace<Key>AtIndexes:with<Key>:
-(void)replaceNameArrayAtIndexes:(NSIndexSet *)indexes withNameArray:(NSArray *)array{
[self.nameArray replaceObjectsAtIndexes:indexes withObjects:array];
}
//替換一個(gè)對(duì)象 replaceObjectIn<Key>AtIndex:withObject:
-(void)replaceObjectInNameArrayAtIndex:(NSUInteger)index withObject:(id)object{
[self.nameArray replaceObjectAtIndex:index withObject:object];
}
KVC底層原理分析
setValue:forKey:賦值原理如下:
1.去模型中查找有沒(méi)有對(duì)應(yīng)的setter方法:例如:setIcon方法,有就直接調(diào)用這個(gè)setter方法給模型這個(gè)屬性賦值[self setIcon:dic[@"icon"]];
2.如果找不到setter方法,接著就會(huì)去尋找有沒(méi)有icon屬性,如果有,就直接訪問(wèn)模型中的icon屬性,進(jìn)行賦值,icon=dict[@"icon"];
3.如果找不到icon屬性,接著又會(huì)去尋找_icon屬性,如果有,直接進(jìn)行賦值_icon=dict[@"icon"];
4.如果都找不到就會(huì)報(bào)錯(cuò):[<Flag 0X7fb74bc7a2c0> setValue:forUndefinedKey:]
如果對(duì)某個(gè)類,不允許使用KVC,可以通過(guò)設(shè)置 accessInstanceVariablesDirectly 控制。