一、KVC:键值编码
1、创建三个类:Person、Car 和 JsonModel
Car类的实现部分如下:
#import "Car.h"
@interface Car()
@property (nonatomic, copy) NSString * carName;
@property (nonatomic, assign)int carPrice;
@end
@implementation Car
@end
Person类的实现部分如下:
#import "Person.h"
#import "Car.h"
@interface Person()
@property (nonatomic, copy) NSString * name;
@property (nonatomic, copy) NSString* pass;
@property (nonatomic, strong) NSDate * birth;
@property (nonatomic, copy,readonly) NSString * readName;
@property (nonatomic,assign) int price;
@property (nonatomic, strong) Car * car;//包含一个Car的实例对象
@end
@implementation Person
//对不存在的key赋值调用此方法
-(void)setValue:(id)value forUndefinedKey:(NSString *)key{
NSLog(@"value = %@",value);
NSLog(@"key = %@",key);
if ([key isEqualToString:@"noExist"]) {
NSLog(@"对不存在的key进行处理");
}
}
//对不存在的key取值调用此方法
-(id)valueForUndefinedKey:(NSString *)key{
NSLog(@"key = %@",key);
if ([key isEqualToString:@"noExist"]) {
NSLog(@"对不存在的key进行处理");
}
return nil;
}
//处理value为nil时调用此方法(比如给int 类型赋值nil就会调用此方法)
-(void)setNilValueForKey:(NSString *)key{
//对price进行处理
if ([key isEqualToString:@"price"]) {
key = 0;
}
else{
[super setNilValueForKey:key];
}
}
@end
JsonModel类的接口部分如下:
#import <Foundation/Foundation.h>
@interface JsonModel : NSObject
@property (nonatomic, strong) NSString * name;
@property (nonatomic, assign) int age;
@property (nonatomic, strong) NSArray * cars;
@end
ViewController类的实现部分:
#import "ViewController.h"
#import "Person.h"
#import "Car.h"
#import "JsonModel.h"
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
Person * person = [[Person alloc] init];
//person.name = @"大圣";//私有属性是无法赋值的
//1、给私有属性赋值和取值
[person setValue:@"大圣" forKey:@"name"];
[person setValue:@"1455" forKey:@"pass"];
[person setValue:[[NSDate alloc] init] forKey:@"birth"];
NSLog(@"person.name = %@",[person valueForKey:@"name"]);
NSLog(@"person.path = %@",[person valueForKey:@"pass"]);
NSLog(@"person.birth = %@",[person valueForKey:@"birth"]);
//person.readName = @"read";//只读属性是无法赋值的
//2、给只读属性赋值和取值
[person setValue:@"read" forKey:@"readName"];
NSLog(@"readName = %@",[person valueForKey:@"readName"]);
//3、给不存在的属性赋值和取值
[person setValue:@"noExistValue" forKey:@"noExist"];
NSLog(@"noExist = %@",[person valueForKey:@"noExist"]);
//4、当value为nil时如何处理
[person setValue:nil forKey:@"price"];
NSLog(@"price = %@",[person valueForKey:@"price"]);
//5、给可变字典赋值和取值
NSMutableDictionary * dic = [[NSMutableDictionary alloc] init];
[dic setValue:@"value1" forKey:@"key1"];// 类似于[dict setObject:@"value1" forKey:@"key1"];
[dic setValue:@"value2" forKey:@"key2"];
NSLog(@"key1的Value值为 = %@",[dic valueForKey:@"key1"]);// 类似于 [dict objectForKey:@"key1”];
NSLog(@"key2的Value值为 = %@",[dic valueForKey:@"key2"]);
//6、用字典给一个模型赋值,json解析时可用
NSDictionary * jsonDict = @{
@"name":@"Jack",
@"age":@18,
@"cars":@[@"BMW",@"Benz"]
};
JsonModel * jsonModel = [[JsonModel alloc] init];
[jsonModel setValuesForKeysWithDictionary:jsonDict];
NSLog(@"jsonModel.name = %@",jsonModel.name);
NSLog(@"jsonModel.age = %d",jsonModel.age);
NSLog(@"jsonModel.cars = %@",jsonModel.cars);
//7-1、key路径给复合属性赋值和取值
[person setValue:[[Car alloc] init] forKey:@"car"];//只有给car实例化后才能给car的属性赋值
[person setValue:@"BMW" forKeyPath:@"car.carName"];//通过key路径给car.carName赋值
NSLog(@"car.name = %@",[person valueForKeyPath:@"car.carName"]);//通过key路径对car.carName取值。
//7-2、key路径与字典
NSArray *arr = @[
@{
@"city":@"beijing",
@"person":@{
@"name":@"zhangsan",
@"city":@"guangzhou"
}
},
@{
@"city":@"chengdu"
}
];
NSArray * arrCitys = [arr valueForKeyPath:@"city"] ;
//可以获取到arrCitys数组 @[@"beijing",@"guangzhou",@"chengdu"]
NSLog(@"arrCitys = %@",arrCitys);
//7-3、key路径与数组
NSArray * array = @[@1,@2,@3,@4,@5];
CGFloat sum = [[array valueForKeyPath:@"@sum.floatValue"] floatValue];//求和
CGFloat avg = [[array valueForKeyPath:@"@avg.floatValue"] floatValue];//平均值
CGFloat max =[[array valueForKeyPath:@"@max.floatValue"] floatValue];//最大值
CGFloat min =[[array valueForKeyPath:@"@min.floatValue"] floatValue];//最小值
NSLog(@"sum = %f,avg = %f,max = %f,min = %f",sum,avg,max,min);
/*
PS:
以Person类的name属性为例
1、程序优先考虑调用setName:方法来进行赋值
2、如果没有setName:方法会搜索名称为_name成员变量,不论该成员变量是在接口部分还是实现部分也不论用什么控制符修饰
3、如果_name不存在,就会搜索名称为name的成员变量,不论该成员变量是在接口部分还是实现部分也不论用什么控制符修饰
4、如果上述三条都不满足就会调用setValue:forUndefineKey:方法,要想对不存在的属性进行处理在Person类内的实现部分重写此方法
*/
/*
PS:
以name属性为例
1、程序优先考虑调用name:方法来进行赋值
2、如果没有name:方法会搜索名称为_name成员变量,不论该成员变量是在接口部分还是实现部分也不论用什么控制符修饰
3、如果_name不存在,就会搜索名称为name的成员变量,不论该成员变量是在接口部分还是实现部分也不论用什么控制符修饰
4、如果上述三条都不满足就会调用valueforUndefineKey:方法,要想对不存在的属性进行处理在Person类内的实现部分重写此方法
*/
}
二、KVO:键值监听
将Person类的car属性移到接口文件,
在Car类的接口文件增加NSString * color;和NSString * speed;两个属性
在Person类的实现文件内增加如下方法
//设置person.car的时候就给car添加键值观察者
-(void)setCar:(Car *)car{
_car = car;
//新值旧值一起观察,即可以将新的value和就的value都传到change数组,这样在调用方法中就可以拿到这两个值
[_car addObserver:self forKeyPath:@"color" options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld context:nil];
[_car addObserver:self forKeyPath:@"speed" options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld context:nil];
}
//只要person.car.color属性或者person.car.speed属性发生改变就会调用这个方法
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context{
NSLog(@"调用了观察者方法");
NSLog(@"keyPath = %@",keyPath);
NSLog(@"object = %@",object);
NSLog(@"change = %@",change);
NSLog(@"new = %@",change[@"new"]);
NSLog(@"old = %@",[change objectForKey:@"old"]);
NSLog(@"context = %@",context);
}
- (void)dealloc
{
//移除监听者
[self.car removeObserver:self forKeyPath:@"color"];
[self.car removeObserver:self forKeyPath:@"speed"];
}
在ViewController.m文件内增加如下方法
//创建person实例及person.car实例并赋值
-(void)kvoTest{
[self createButton];
Car * car = [[Car alloc] init];
self.car = car;
car.color = @"white";
Person * person = [[Person alloc] init];
person.car = car;//此行代码会为person对象的car属性添加观察者
self.person = person;
}
//创建button
-(void)createButton{
UIButton * myBtn = [UIButton buttonWithType:UIButtonTypeCustom];
myBtn.frame = CGRectMake(40, 80, 100, 50);
[myBtn setTitle:@"按钮" forState:UIControlStateNormal];
myBtn.backgroundColor = [UIColor blueColor];
[myBtn addTarget:self action:@selector(clickBtn:) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:myBtn];
}
//button的点击事件
-(void)clickBtn:(UIButton*)btn{
//只要person.car的属性发生改变就会调用观察者方法
self.person.car.color = @"black";
self.person.car.speed = 200;
}
参考文献:
1、KVC/KVO原理详解及编程指南 - 王中周的个人博客 - 博客频道 - CSDN.NET