@chenbinghua
2015-09-12T02:34:12.000000Z
字数 3615
阅读 1822
iOS笔记
作用:存取对象的属性
除了一般的赋值和取值的方法,我们还可以用Key-Value-Coding(KVC)键值编码来访问你要存取的成员属性,甚至是受保护的,没有getter和setter方法访问的成员属性。
在核心动画中,KVC大量使用,通常是通过KVC设置动画的KeyPath。
新建一个Student类
Student.h
#import <Foundation/Foundation.h>@interface Student : NSObject{// 默认是@protected,不能被外界访问NSString *name;}@end
Student.m
#import "Student.h"@implementation Student@end
NSString *name成员变量默认是@protected修饰,也没有getter和setter方法访问,所以不能用结构体的->或点语法访问,但是可以用KVC访问。
#import <Foundation/Foundation.h>#import "Student.h"int main(int argc, const char * argv[]) {Student *student = [[Student alloc]init ];[student setValue:@"张三" forKey:@"name"];// student->name; 报错// 没有getter和setter方法,也就没有点语法访问NSString *name = [student valueForKey:@"name"];NSLog(@"学生姓名:%@",name);return 0;}
如果存的时候key和类属性的名称不一致会怎么样呢?
代码改成
[student setValue:@"张三" forKey:@"name1"];
运行,程序崩溃 ,打印:
2015-09-04 18:52:26.356 KVC[5203:259489] *** Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[<Student 0x100206980> setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key name1.'
这个原因this class is not key value coding-compliant for the key name1是不是很熟悉
// IBOutlet底层实现
如果访问这个类里中的属性中的属性呢?那就用到了键路径
关键字:键路径取值valueForKeyPath 键路径存值:forKeyPath
// Student有属性Course *course,Course有[student setValue:@"数学课" forKeyPath:@"course.CourseName"];
// Student有属性NSInteger point;[student setValue:@"88" forKeyPath:@"point"]; NSString *point = [student valueForKey:@"point"];
#import "Student.h"@implementation Student{NSString *name;NSInteger point;NSArray *otherStudent;}@end
Student *student = [[Student alloc]init];Student *student1 = [[Student alloc]init];Student *student2 = [[Student alloc]init];Student *student3 = [[Student alloc]init];[student1 setValue:@"65" forKey:@"point"];[student2 setValue:@"77" forKey:@"point"];[student3 setValue:@"99" forKey:@"point"];NSArray *array = @[student1,student2,student3];[student setValue:array forKey:@"otherStudent"];NSLog(@"其他学生的成绩%@", [student valueForKeyPath:@"otherStudent.point"]);// (65、77、99)NSLog(@"共%@个学生", [student valueForKeyPath:@"otherStudent.@count"]);// 共3个学生NSLog(@"最高成绩:%@", [student valueForKeyPath:@"otherStudent.@max.point"]);// 99NSLog(@"最低成绩:%@", [student valueForKeyPath:@"otherStudent.@min.point"]);// 64NSLog(@"平均成绩:%@", [student valueForKeyPath:@"otherStudent.@avg.point"]);// 80.333333333333333333333333333333333333NSLog(@"平均成绩:%@", [student valueForKeyPath:@"otherStudent.@sum.point"]);// 241
key:value
1.首先在模型中查找是否有setValue:方法,有就调用setValue:方法赋值
1.如果没有setValue:方法,就查找是否有_value属性
3.如果没有_value属性,就查找value属性
4.如果还没有就报错
- (void)setValuesForKeysWithDictionary:(NSDictionary<NSString *, id> *)keyedValues;
setValuesForKeysWithDictionary:方法就是遍历字典里面的所有key,遍历操作就是调用setValue:forKey:方法,如果没找到属性赋值就会报错。
当字典或JSON数据中有关键字,如
{"id" : "tripleCC","age" : "30","address" : "杭州","schooll" : "HDU"...}
方法一:可以重写setValue:forKey:方法
- (void)setValue:(id)value forKey:(NSString *)key{if ([key isEqualToString:@"id"]) {[self setValue:value forKeyPath:@"ID"];}else{[super setValue:value forKey:key];}}
方法二:使用runtime
MJExtention实现原理
遍历模型中属性名,遍历操作是根据属性名去字典中找,找到就赋值,可以传进来的字典改变属性名对应字典中的值
抽成NSObject的分类
#import "NSObject+Model.h"#import <objc/runtime.h>@implementation NSObject (Model)+ (instancetype)objcWithDict:(NSDictionary *)dict mapDict:(NSDictionary *)mapDict{id objc = [[self alloc] init];// 遍历模型中属性unsigned int count = 0;Ivar *ivars = class_copyIvarList(self, &count);for (int i = 0 ; i < count; i++) {Ivar ivar = ivars[i];// 属性名称NSString *ivarName = @(ivar_getName(ivar));// 获取出来的是`_`开头的成员变量名,需要截取`_`之后的字符串ivarName = [ivarName substringFromIndex:1];id value = dict[ivarName];// 需要由外界通知内部,模型中属性名对应字典里面的哪个key// ID -> idif (value == nil) {if (mapDict) {NSString *keyName = mapDict[ivarName];value = dict[keyName];}}[objc setValue:value forKeyPath:ivarName];}return objc;}@end