“这是我参与8月更文挑战的第3天,活动详情查看:8月更文挑战”
前言
KVC
又称键值编码 (Key-Value-Coding)
,在iOS开发中是一个比较常见的技术点,相信很多开发人员都使用过KVC
,其主要的两个方法就是如下两个,分别对应设置值和取值:
(void)setValue:(nullable id)value forKey:(NSString *)key;
(nullable id)valueForKey:(NSString *)key;
一.KVC
简单应用
LGPerson *person = [[LGPerson alloc] init];
// 一般setter 方法
person.name = @"LG_Cooci"; // setter -- llvm
person.age = 18;
person->myName = @"cooci";
NSLog(@"%@ - %d - %@",person.name,person.age,person->myName);
// 1:Key-Value Coding (KVC) : 基本类型 - 看底层原理
// 非正式协议 - 间接访问
[person setValue:@"KC" forKey:@"name"];
// 2:KVC - 集合类型
person.array = @[@"1",@"2",@"3"];
// 修改数组
// person.array[0] = @"100";
// 第一种:搞一个新的数组 - KVC 赋值就OK
NSArray *array = [person valueForKey:@"array"];
array = @[@"100",@"2",@"3"];
[person setValue:array forKey:@"array"];
NSLog(@"%@",[person valueForKey:@"array"]);
// 第二种
NSMutableArray *mArray = [person mutableArrayValueForKey:@"array"];
mArray[0] = @"200";
NSLog(@"%@",[person valueForKey:@"array"]);
// 3:KVC - 集合操作符
// 4:KVC - 访问非对象属性 - 面试可能问到
// 结构体
ThreeFloats floats = {1.,2.,3.};
NSValue *value = [NSValue valueWithBytes:&floats objCType: **@encode**(ThreeFloats)];
[person setValue:value forKey:@"threeFloats"];
NSValue *value1 = [person valueForKey:@"threeFloats"];
NSLog(@"%@",value1);
ThreeFloats th;
[value1 getValue:&th];
NSLog(@"%f-%f-%f",th.x,th.y,th.z);
// 5:KVC - 层层访问 - keyPath
LGStudent *student = [LGStudent alloc];
student.subject = @"大师班";
person.student = student;
[person setValue:@"Swift" forKeyPath:@"student.subject"];
NSLog(@"%@",[person valueForKeyPath:@"student.subject"]);
#pragma mark **- array取值**
- (**void**)arrayDemo{
LGStudent *p = [LGStudent 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:@"pen9"]);
// 遍历
NSEnumerator *enumerator = [arr objectEnumerator];
NSString* str = **nil**;
**while** (str = [enumerator nextObject]) {
NSLog(@"%@", str);
}
}
#pragma mark **- 字典操作**
- (**void**)dictionaryTest{
NSDictionary* dict = @{
@"name":@"Cooci",
@"nick":@"KC",
@"subject":@"iOS",
@"age":@18,
@"length":@180
};
LGStudent *p = [[LGStudent alloc] init];
// 字典转模型
[p setValuesForKeysWithDictionary:dict];
NSLog(@"%@",p);
// 键数组转模型到字典
NSArray *array = @[@"name",@"age"];
NSDictionary *dic = [p dictionaryWithValuesForKeys:array];
NSLog(@"%@",dic);
}
#pragma mark **- KVC消息传递**
- (**void**)arrayMessagePass{
NSArray *array = @[@"Hank",@"Cooci",@"Kody",@"CC"];
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{
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);
}
// 数组操作符 @distinctUnionOfObjects @unionOfObjects
- (**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);
}
// 嵌套集合(array&set)操作 @distinctUnionOfArrays @unionOfArrays @distinctUnionOfSets
- (**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++) {
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{
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++) {
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
四.KVC
设值和取值过程
KVC
设置
KVC
设置值的过程
执行的优先顺序按照苹果官方文档所示set<Key>:
-> _set<key>:
当 accessInstanceVariablesDirectly
设置为YES
时 获取实例变量的顺序为顺序查找名称为_<key>
->_is<Key>
-><key>
->is<Key>
_<key>
_is<Key>
<key>
is<Key>
按照官方文档说 如果没有以上都没有 就会进入下面这个方法
KVC
取值的过程
执行顺序按照官方文档 get<Key>
-><key>
->is<Key>
->_<key>
如果以上都没有 按照_<key>
->_is<Key>
-><key>
->is<Key>
取
细节当我们注释掉_name
打印name
显示的是_isName
顺序问题
如果都没有的话 就会调用 valueForUndefinedKey:
这个方法
五.KVC
自定义实现
- (**void**)lg_setValue:(**nullable** **id**)value forKey:(NSString *)key{
// KVC 自定义
// 1: 判断什么 key
**if** (key == **nil** || key.length == 0) {
**return**;
}
// 2: setter set<Key>: or _set<Key>,
// key 要大写
NSString *Key = key.capitalizedString;
// 拼接方法
NSString *setKey = [NSString stringWithFormat:@"set%@:",Key];
NSString *_setKey = [NSString stringWithFormat:@"_set%@:",Key];
NSString *setIsKey = [NSString stringWithFormat:@"setIs%@:",Key];
**if** ([**self** lg_performSelectorWithMethodName:setKey value:value]) {
NSLog(@"*********%@**********",setKey);
**return**;
}**else** **if** ([**self** lg_performSelectorWithMethodName:_setKey value:value]) {
NSLog(@"*********%@**********",_setKey);
**return**;
}**else** **if** ([**self** lg_performSelectorWithMethodName:setIsKey value:value]) {
NSLog(@"*********%@**********",setIsKey);
**return**;
}
// 3: 判断是否响应 accessInstanceVariablesDirectly 返回YES NO 奔溃
// 3:判断是否能够直接赋值实例变量
**if** (![**self**.class accessInstanceVariablesDirectly] ) {
**@throw** [NSException exceptionWithName:@"LGUnknownKeyException" reason:[NSString stringWithFormat:@"****[%@ valueForUndefinedKey:]: this class is not key value coding-compliant for the key name.****",**self**] userInfo:**nil**];
}
// 4: 间接变量
// 获取 ivar -> 遍历 containsObjct -
// 4.1 定义一个收集实例变量的可变数组
NSMutableArray *mArray = [**self** getIvarListName];
// _<key> _is<Key> <key> is<Key>
NSString *_key = [NSString stringWithFormat:@"_%@",key];
NSString *_isKey = [NSString stringWithFormat:@"_is%@",Key];
NSString *isKey = [NSString stringWithFormat:@"is%@",Key];
**if** ([mArray containsObject:_key]) {
// 4.2 获取相应的 ivar
Ivar ivar = class_getInstanceVariable([**self** class], _key.UTF8String);
// 4.3 对相应的 ivar 设置值
object_setIvar(**self** , ivar, value);
**return**;
}**else** **if** ([mArray containsObject:_isKey]) {
Ivar ivar = class_getInstanceVariable([**self** class], _isKey.UTF8String);
object_setIvar(**self** , ivar, value);
**return**;
}**else** **if** ([mArray containsObject:key]) {
Ivar ivar = class_getInstanceVariable([**self** class], key.UTF8String);
object_setIvar(**self** , ivar, value);
**return**;
}**else** **if** ([mArray containsObject:isKey]) {
Ivar ivar = class_getInstanceVariable([**self** class], isKey.UTF8String);
object_setIvar(**self** , ivar, value);
**return**;
}
// 5:如果找不到相关实例
**@throw** [NSException exceptionWithName:@"LGUnknownKeyException" reason:[NSString stringWithFormat:@"****[%@ %@]: this class is not key value coding-compliant for the key name.****",**self**,NSStringFromSelector( **_cmd**)] userInfo:**nil**];
}
- (**nullable** **id**)lg_valueForKey:(NSString *)key{
// 1:刷选key 判断非空
**if** (key == **nil** || key.length == 0) {
**return** **nil**;
}
// 2:找到相关方法 get<Key> <key> countOf<Key> objectIn<Key>AtIndex
// key 要大写
NSString *Key = key.capitalizedString;
// 拼接方法
NSString *getKey = [NSString stringWithFormat:@"get%@",Key];
NSString *countOfKey = [NSString stringWithFormat:@"countOf%@",Key];
NSString *objectInKeyAtIndex = [NSString stringWithFormat:@"objectIn%@AtIndex:",Key];
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
**if** ([**self** respondsToSelector:NSSelectorFromString(getKey)]) {
**return** [**self** performSelector:NSSelectorFromString(getKey)];
}**else** **if** ([**self** respondsToSelector:NSSelectorFromString(key)]){
**return** [**self** performSelector:NSSelectorFromString(key)];
}**else** **if** ([**self** respondsToSelector:NSSelectorFromString(countOfKey)]){
**if** ([**self** respondsToSelector:NSSelectorFromString(objectInKeyAtIndex)]) {
**int** num = (**int**)[**self** performSelector:NSSelectorFromString(countOfKey)];
NSMutableArray *mArray = [NSMutableArray arrayWithCapacity:1];
**for** (**int** i = 0; i<num-1; i++) {
num = (**int**)[**self** performSelector:NSSelectorFromString(countOfKey)];
}
**for** (**int** j = 0; j<num; j++) {
**id** objc = [**self** performSelector:NSSelectorFromString(objectInKeyAtIndex) withObject:@(num)];
[mArray addObject:objc];
}
**return** mArray;
}
}
#pragma clang diagnostic pop
// 3:判断是否能够直接赋值实例变量
**if** (![**self**.class accessInstanceVariablesDirectly] ) {
**@throw** [NSException exceptionWithName:@"LGUnknownKeyException" reason:[NSString stringWithFormat:@"****[%@ valueForUndefinedKey:]: this class is not key value coding-compliant for the key name.****",**self**] userInfo:**nil**];
}
// 4.找相关实例变量进行赋值
// 4.1 定义一个收集实例变量的可变数组
NSMutableArray *mArray = [**self** getIvarListName];
// _<key> _is<Key> <key> is<Key>
// _name -> _isName -> name -> isName
NSString *_key = [NSString stringWithFormat:@"_%@",key];
NSString *_isKey = [NSString stringWithFormat:@"_is%@",Key];
NSString *isKey = [NSString stringWithFormat:@"is%@",Key];
**if** ([mArray containsObject:_key]) {
Ivar ivar = class_getInstanceVariable([**self** class], _key.UTF8String);
**return** object_getIvar(**self**, ivar);;
}**else** **if** ([mArray containsObject:_isKey]) {
Ivar ivar = class_getInstanceVariable([**self** class], _isKey.UTF8String);
**return** object_getIvar(**self**, ivar);;
}**else** **if** ([mArray containsObject:key]) {
Ivar ivar = class_getInstanceVariable([**self** class], key.UTF8String);
**return** object_getIvar(**self**, ivar);;
}**else** **if** ([mArray containsObject:isKey]) {
Ivar ivar = class_getInstanceVariable([**self** class], isKey.UTF8String);
**return** object_getIvar(**self**, ivar);;
}
**return** @"";
}
#pragma mark **- 相关方法**
- (**BOOL**)lg_performSelectorWithMethodName:(NSString *)methodName value:(**id**)value{
**if** ([**self** respondsToSelector:NSSelectorFromString(methodName)]) {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
[**self** performSelector:NSSelectorFromString(methodName) withObject:value];
#pragma clang diagnostic pop
**return** **YES**;
}
**return** **NO**;
}
- (**id**)performSelectorWithMethodName:(NSString *)methodName{
**if** ([**self** respondsToSelector:NSSelectorFromString(methodName)]) {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
**return** [**self** performSelector:NSSelectorFromString(methodName) ];
#pragma clang diagnostic pop
}
**return** **nil**;
}
- (NSMutableArray *)getIvarListName{
NSMutableArray *mArray = [NSMutableArray arrayWithCapacity:1];
**unsigned** **int** count = 0;
Ivar *ivars = class_copyIvarList([**self** class], &count);
**for** (**int** i = 0; i<count; i++) {
Ivar ivar = ivars[i];
**const** **char** *ivarNameChar = ivar_getName(ivar);
NSString *ivarName = [NSString stringWithUTF8String:ivarNameChar];
NSLog(@"ivarName == %@",ivarName);
[mArray addObject:ivarName];
}
free(ivars);
**return** mArray;
}