iOS-OC底层17:KVC原理
2020-10-26 本文已影响0人
MonKey_Money
KVC 简介
KVC全称是Key Value Coding(键值编码),是一个基于NSKeyValueCoding非正式协议实现的机制,它可以直接通过key值对对象的属性进行存取操作,而不需通过调用明确的存取方法。这样就可以在运行时动态在访问和修改对象的属性,而不是在编译时确定。
KVC提供了一种间接访问属性方法或成员变量的机制,可以通过字符串来访问对象的的属性方法或成员变量。
在实现了访问器方法的类中,使用点语法和KVC访问对象其实差别不大,二者可以任意混用(因为KVC会首先搜索访问器方法,)。但是没有访问器方法的类中,点语法无法使用,这时KVC就有优势了。
KVC和KVO都是基于OC的动态特性和Runtime机制的。
基本使用
创建一个类
@interface MyPerson : NSObject
@property (nonatomic, copy) NSString *name;
@property (nonatomic, copy) NSString *hobby;
@property (nonatomic, copy) NSArray *array;
@end
通过keyvaluecode设值 取值
MyPerson *person = [[MyPerson alloc] init];
[person setValue:@"cooci" forKey:@"name"];
[person setValue:@"篮球" forKey:@"hobby"];
NSLog(@"%@的爱好是%@",[person valueForKey:@"name"],[person valueForKey:@"hobby"]);
NSDictionary *dictionary = @{
@"name":@"hank",
@"hobby":@"足球",
};
MyPerson *person2 = [[MyPerson alloc] init];
[person2 setValuesForKeysWithDictionary:dictionary];
NSLog(@"%@的爱好是%@",person2.name,person2.hobby);
NSArray *keyArray = @[@"name",@"hobby"];
NSDictionary *valueDictionary = [person2 dictionaryWithValuesForKeys:keyArray];
NSLog(@"value 值%@",valueDictionary);
打印结果
cooci的爱好是篮球
hank的爱好是足球
value 值{
hobby = "\U8db3\U7403";
name = hank;
}
修改数组
MyPerson *person = [[MyPerson alloc] init];
person.array = @[@"1",@"2",@"3"];
//取值
NSLog(@"%@",[person valueForKey:@"array"]);
//直接通过setValue forKey修改
NSArray *array1 = @[@"2",@"2",@"3"];
[person setValue:array1 forKey:@"array"];
NSLog(@"%@",person.array);
//取出数组修改
NSMutableArray *mutableArray = [person mutableArrayValueForKey:@"array"];
mutableArray[0] = @"200";
NSLog(@"%@",person.array);
打印结果
(
1,
2,
3
)
(
2,
2,
3
)
(
200,
2,
3
)
集合操作符
NSArray *array = @[@"Hank",@"Cooci",@"Kody",@"CC"];
//获取数组内字符串的长度
NSArray *lenStr= [array valueForKeyPath:@"length"];
NSLog(@"%@",lenStr);
//让数组内的字符串小写
NSArray *lowStr= [array valueForKeyPath:@"lowercaseString"];
NSLog(@"%@",lowStr);
打印结果
(
4,
5,
4,
2
)
(
hank,
cooci,
kody,
cc
)
对数组中对象的属性聚合操作
- (void)aggregationOperator{
NSMutableArray *personArray = [NSMutableArray array];
for (int i = 0; i < 6; i++) {
LGStudent *p = [LGStudent 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"]);
/// 平均身高
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);
}
打印结果
(
183,
181,
175,
181,
185,
179
)
2020-10-26 15:49:50.477336+0800 001-KVC简介[92636:366961] 180.666672
2020-10-26 15:49:50.478062+0800 001-KVC简介[92636:366961] 6
2020-10-26 15:49:50.478566+0800 001-KVC简介[92636:366961] 1084
2020-10-26 15:49:50.479016+0800 001-KVC简介[92636:366961] 185
2020-10-26 15:49:50.480592+0800 001-KVC简介[92636:366961] 175
数组操作符
- (void)arrayOperator{
NSMutableArray *personArray = [NSMutableArray array];
for (int i = 0; i < 6; i++) {
LGStudent *p = [LGStudent 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);
}
打印
(
175,
185,
183,
175,
181,
185
)
arr1 = (
175,
185,
183,
175,
181,
185
)
arr2 = (
175,
185,
181,
183
)
数组中嵌套数组
- (void)arrayNesting{
NSMutableArray *personArray1 = [NSMutableArray array];
for (int i = 0; i < 6; i++) {
LGStudent *student = [LGStudent new];
NSDictionary* dict = @{
@"name":@"Tom",
@"age":@(18+i),
@"nick":@"Cat",
@"length":@(175 + 2*arc4random_uniform(6)),
};
[student setValuesForKeysWithDictionary:dict];
[personArray1 addObject:student];
}
NSMutableArray *personArray2 = [NSMutableArray array];
for (int i = 0; i < 6; i++) {
LGStudent *person = [LGStudent 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);
}
打印结果
arr = (
185,
181,
179,
175
)
arr1 = (
185,
175,
185,
175,
181,
175,
175,
179,
179,
181,
179,
175
)
集合中嵌套集合
- (void)setNesting{
NSMutableSet *personSet1 = [NSMutableSet set];
for (int i = 0; i < 6; i++) {
LGStudent *person = [LGStudent 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++) {
LGStudent *person = [LGStudent 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);
}
打印结果
personSet1 = {(
181,
177,
183,
179
)}
personSet2 = {(
181,
177,
183,
179
)}
arr1 = {(
181,
177,
183,
179
)}
KVC原理
kvc的设值和取值流程可以参看
setter设值顺序
1.查找 set<Key>: 或 _set<Key>: 命名的 setter,按照这个顺序,如果找到,则调用这个方法并将值传进去。
2.如果没有发现一个简单的 setter ,但是 accessInstanceVariablesDirectly 类属性返回YES,则查找一个命名规则为 _key、_isKey、key、isKey的实例变量。按照这个顺序,如果查找到则将value赋值给实例变量。
3.如果没有找到 setter 或实例变量,则调用 setValue:forUndefinedKey: 方法,并默认抛出一个异常。
getter取值流程
- 首先按 get<Key>、<key>、is<Key> 的顺序查找 getter 方法,找到直接调用。
• 若方法的返回结果类型是一个对象指针,则直接返回结果。
• 若类型为能够转化为 NSNumber 的基本数据类型,转换为 NSNumber 后返回;否则转换为 NSValue 返回。 - 若上面的 getter没有找到,则查找 countOf<Key>、objectIn<Key>AtIndex:、<Key>AtIndexes 格式的方法。如果 countOf<Key> 和另外两个方法中的一个找到,那么就会返回一个可以响应 NSArray 所有方法的集合代理。发送给这个代理集合的 NSArray 消息方法,就会以 countOf<Key>、objectIn<Key>AtIndex:、<Key>AtIndexes 这几个方法组合的形式调用。如果 receiver 的类实现了 get<Key>:range: 方法,该方法也会用于性能优化。
- 还没查到,那么查找 countOf<Key>、enumeratorOf<Key>、memberOf<Key>: 格式的方法。如果这3个方法都找到,那么久返回一个可以相应NSSet所有方法的集合代理。发送给这个代理集合的NSSet消息方法,就会以countOf<Key>、enumeratorOf<Key>、memberOf<Key>: 组合的形式调用。
- 还是没查到,那么如果类方法 accessInstanceVariablesDirectly返回YES,那么按_<key>、_is<Key>、<key>、is<Key> 的顺序直接搜索实例变量。如果搜索到了,则返回receiver相应实例变量的值。
- 再没有查到,调用 valueForUndefinedKey: 方法,抛出异常。