对于KVC,面试中会遇到,以前以为自己很了解,但当人家问到就老蒙圈,现在总结一下,以下内容来自或官方文档,或技术博客,或个人发挥。。。
一、访问成员变量
1)定义一个Student类,继承于NSObject。
.h
@interface Student : NSObject { NSString *name; } @property (nonatomic, assign) int age; @end
.m
#import "Student.h" @implementation Student @end
2)想要访问name,咋办呢?用KVC。 main中代码如下:
#import "Student.h" int main(int argc, const char * argv[]) { @autoreleasepool { Student *student = [[Student alloc]init]; [student setValue:@"Doris" forKey:@"name"]; NSString *name = [student valueForKey:@"name"]; NSLog(@"name:%@",name); } return 0; }
打印结果:
2016-03-10 17:23:51.518 KVC[9547:394677] name:Doris
3)现在name通过KVC赋值成功,也能用valueForKey取出值。那我们赋值的这个name是在Student中定义的成员变量name吗?添加一段代码试一下:
[student setValue:@"男" forKey:@"sex"]; NSString *sex = [student valueForKey:@"sex"]; NSLog(@"student sex:%@",sex);
编译没错,运行发现程序崩溃了!,错误如下:
*** Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[<Student 0x100604b60> setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key sex.'
提示没有这个sex 这个key,所以KVC赋值的一定是存在的成员变量或属性。下面看一下age这个属性,直接用点语法访问,也可以用KVC。(后面两种纯属实验)
student.age = 11; int age1 = student.age; NSLog(@"%d",age1); [student setValue:@"22" forKey:@"age"]; NSString *age2 = [student valueForKey:@"age"]; NSLog(@"age2:%@",age2); [student setValue:@"33" forKey:@"_age"]; NSString *age3 = [student valueForKey:@"_age"]; NSLog(@"age3:%@",age3);
运行结果:
2016-03-10 17:34:53.829 KVC[9591:400420] 11
2016-03-10 17:34:53.829 KVC[9591:400420] age2:22
2016-03-10 17:34:53.829 KVC[9591:400420] age3:33
当然了,在.m里,匿名类中定义的属性和成员变量都可以通过KVC的方式进行访问。
4)实际应用例子,修改UISearchBar的placeHolder字体颜色:
//修改placeholder的颜色 UITextField *searchField = [_searchBar valueForKey:@"_searchField"]; [searchField setValue:RGBACOLOR(184, 184, 186, 1) forKeyPath:@"_placeholderLabel.textColor"];
通过这种方式可以修改很多系统控件。
二、键路径访问属性
如果访问这个类里中的属性中的属性呢?那就用到了键路径
关键字:键路径取值valueForKeyPath 键路径存值:forKeyPath
1) 新建一个类Course,课程类,课程类有课程名称这个属性
.h
@interface Course : NSObject { NSString *CourseName; } @end
.m
#import "Course.h" @implementation Course @end
2) 在Student中添加Course属性 ,student.h文件中代码如下:
#import "Course.h" @interface Student : NSObject { NSString *name; Course *course; }
.m没有改动
3)
在main方法中,我们实验通过键路径访问Course中CourseName的属性
Course *course = [[Course alloc]init]; [course setValue:@"语文" forKey:@"courseName"]; [student setValue:course forKey:@"course"]; NSString *courseName = [student valueForKeyPath:@"course.courseName"]; NSLog(@"课程名称:%@", courseName); //键路径赋值 [student setValue:@"数学" forKeyPath:@"course.courseName"]; courseName = [student valueForKeyPath:@"course.courseName"]; NSLog(@"课程名称:%@", courseName);
运行结果正确^_^
三、自动封装基本数据类型
1)我们在Student类中添加分数属性 NSInteger point;.m不变,然后进行存取。
2)上面的age属性已经展示了,我们用NSString*类型设置的属性值@"22",而我们的属性是int类型的,存取都没有问题。
四、操作集合
这个看起来比较高端。在Student类中加入数组NSArray,用来表示其他的学生。这样我们可以添加多个其他的学生,再用集合操作计算学生的分数,最高分,最低分,平均分,求和等。
//操作集合 Student *student1 = [[Student alloc]init]; Student *student2 = [[Student alloc]init]; Student *student3 = [[Student alloc]init]; [student1 setValue:@"59" forKey:@"point"]; [student2 setValue:@"20" forKey:@"point"]; [student3 setValue:@"100" forKey:@"point"]; NSArray *array = @[student1,student2,student3]; [student setValue:array forKey:@"otherStudent"]; NSLog(@"其他学生的成绩%@", [student valueForKeyPath:@"otherStudent.point"]); NSLog(@"共%@个学生", [student valueForKeyPath:@"otherStudent.@count"]); NSLog(@"最高成绩:%@", [student valueForKeyPath:@"otherStudent.@max.point"]); NSLog(@"最低成绩:%@", [student valueForKeyPath:@"otherStudent.@min.point"]); NSLog(@"平均成绩:%@", [student valueForKeyPath:@"otherStudent.@avg.point"]); NSLog(@"总成绩:%@", [student valueForKeyPath:@"otherStudent.@sum.point"]);
运行结果:
2016-03-10 18:41:19.293 KVC[10313:439192] 其他学生的成绩(
59,
20,
100
)
2016-03-10 18:41:19.293 KVC[10313:439192] 共3个学生
2016-03-10 18:41:19.294 KVC[10313:439192] 最高成绩:100
2016-03-10 18:41:19.294 KVC[10313:439192] 最低成绩:20
2016-03-10 18:41:19.294 KVC[10313:439192] 平均成绩:59.666666666666666666666666666666666666
2016-03-10 18:41:19.294 KVC[10313:439192] 总成绩:179