@chenbinghua
2015-09-12T10:34:12.000000Z
字数 3615
阅读 1631
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"]);// 99
NSLog(@"最低成绩:%@", [student valueForKeyPath:@"otherStudent.@min.point"]);// 64
NSLog(@"平均成绩:%@", [student valueForKeyPath:@"otherStudent.@avg.point"]);// 80.333333333333333333333333333333333333
NSLog(@"平均成绩:%@", [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 -> id
if (value == nil) {
if (mapDict) {
NSString *keyName = mapDict[ivarName];
value = dict[keyName];
}
}
[objc setValue:value forKeyPath:ivarName];
}
return objc;
}
@end