KVC
KVC称为键-值编码(key value coding),一种以字符串标识符间接访问属性的机制
对于kvc机制如何通过key寻找到value:
当通过KVC调用对象时,比如:[self valueForKey:@”someKey”]时,程序会自动试图通过几种不同的方式解析这个调用。
1.首先查找对象是否带有 someKey 这个方法
2.如果没找到,会继续查找对象是否带有someKey这个实例变量(iVar)
3.如果还没有找到,程序会继续试图调用 -(id) valueForUndefinedKey:这个方法。
4.如果这个方法还是没有被实现的话,程序会抛出一个NSUndefinedKeyException异常错误。
(Key-Value Coding查找方法的时候,不仅仅会查找someKey这个方法,还会查找getsomeKey这个方法,前面加一个get,或者_someKey以及_getsomeKey这几种形式。同时,查找实例变量的时候也会不仅仅查找someKey这个变量,也会查找_someKey这个变量是否存在。)
设计valueForUndefinedKey:方法的主要目的是当你使用-(id)valueForKey方法从对象中请求值时,对象能够在错误发生前,有最后的机会响应这个请求。
// 设置值:setValue:forKey:
// 获取值: valueForKey:
[car setValue:@"BMW2.0" forKey:@"type"];
NSLog(@"type = %@",[car valueForKey:@"type"]);
//注意: 对基本类型使用kvc存储NSNumber
[car setValue:@23.4 forKey:@"speed"];
NSLog(@"speed = %f",[[car valueForKey:@"speed"] doubleValue]);
//注意: 出现问题, key在对象不存在,崩溃
[car setValue:@"123” forKey:@"test"];
//keyPath的使用
//需求: 使用KVC设置car的engine的power为300
[car setValue:@300.0 forKeyPath:@"engine.power"];
NSLog(@"power = %f",[[car valueForKeyPath:@"engine.power"] doubleValue]);
//字典转化为对象setValuesForKeysWithDictionary
NSDictionary *carDict = @{@"type":@"Santana",@"speed":@12};
[car setValuesForKeysWithDictionary:carDict];
NSLog(@"type = %@,speed=%f",car.type,car.speed);
//KVC的原理
[car mySetValue:@"benz" forKey:@"type"];
//实现系统和setValue:forKey方法
-(void)mySetValue:(id)object forKey:(NSString *)key
{
//思路:
//1.根据字符串生成 setType:方法
//2.调用这个方法
NSString *methodName = [NSString stringWithFormat:@"set%@:",[key capitalizedString]];
SEL sel = NSSelectorFromString(methodName);
[self performSelector:sel withObject:object];
}
KVO
KVO称为键-值监听(key value observing),观察某个属性的变化的机制,当某个属性一旦改变,可以立即观察到这个属性的改变
//实时观察到车速的改变
car.speed = 0;
//添加对于speed属性的监听
//关键方法: addObserver
//作用: 一旦@"speed"对应属性从旧值变为新值, self对象发送消息(方法名是固定的)
[car addObserver:self forKeyPath:@"speed" options:NSKeyValueObservingOptionOld|NSKeyValueObservingOptionNew context:nil];
//只有在使用set方法时候KVO才能起效
car.speed = 100;
car.speed = 200;
//addObserver 必须在结束之后移除监听
[car removeObserver:self forKeyPath:@"speed"];
//这个方法是kvo的监听处理方法
//参数1: 表示哪个属性改变了
//参数2: 表示哪个对象的属性改变了
//参数3: 表示如何改变的
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context
{
//获取改变值, 使用KVC获取这个值
double speed = [[object valueForKeyPath:keyPath] doubleValue];
NSLog(@"speed = %f",speed);
}
Archiver
archiver即归档:数据持久化(把数据保存到文件中)
保存方式:文件, plist,数据库,NSUserDefault
为了把对象存储到文件,存储方式最好选用归档
//系统对象的归档
//需求: 保存到test.data中
NSArray *array = @[@"aa",@"bb",@"cc",@"dd"];
NSString *path = [NSHomeDirectory() stringByAppendingString:@"/Documents/test.data"];
[NSKeyedArchiver archiveRootObject:array toFile:path];
//解档
NSArray *newArray = [NSKeyedUnarchiver unarchiveObjectWithFile:path];
NSLog(@"newA = %@",newArray);
//自定义对象的归档
//核心: 自定义对象实现归档功能,这个类需要遵守NSCoding,并且是实现两个方法
Car *car = [[Car alloc] init];
car.type = @"Audi";
car.speed = 300;
NSString *path = [NSHomeDirectory() stringByAppendingString:@"car.data"];
//归档时候自动调用encodeWithCoder:方法
[NSKeyedArchiver archiveRootObject:car toFile:path];
//解档时候自动调用initWithCoder:方法
Car *newCar = [NSKeyedUnarchiver unarchiveObjectWithFile:path];
NSLog(@"t=%@ s=%f",newCar.type, newCar.speed);
#pragma mark - 分别在归档和解档的时候调用
//Car遵循<NSCoding>协议,实现两个方法
- (void)encodeWithCoder:(NSCoder *)aCoder
{
//aCoder传入的编码对象
[aCoder encodeObject:self.type forKey:@"type"];
[aCoder encodeObject:[NSNumber numberWithDouble:self.speed] forKey:@"speed"];
}
- (id)initWithCoder:(NSCoder *)aDecoder
{
if(self = [super init])
{
self.type = [aDecoder decodeObjectForKey:@"type"];
self.speed = [[aDecoder decodeObjectForKey:@"speed"] doubleValue];
}
return self;
}
//多个类继承时的归档
FlyCar *flyCar = [[FlyCar alloc] init];
flyCar.type = @"Kirui QQ";
flyCar.speed = 88;
flyCar.flySpeed = 888;
flyCar.flyHeight = 8888;
NSString *path = [NSHomeDirectory() stringByAppendingString:@"/Documents/flyCar.data"];
[NSKeyedArchiver archiveRootObject:flyCar toFile:path];
FlyCar *newFlyCar = [NSKeyedUnarchiver unarchiveObjectWithFile:path];
NSLog(@"%@ %f %f %f", newFlyCar.type, newFlyCar.speed, newFlyCar.flyHeight, newFlyCar.flySpeed);
//FlyCar遵循<NSCoding>协议,实现两个方法
-(void)encodeWithCoder:(NSCoder *)aCoder
{
//子类需要执行父类的编码方法
[super encodeWithCoder:aCoder];
[aCoder encodeObject:[NSNumber numberWithDouble:self.flyHeight] forKey:@"flyHeight"];
[aCoder encodeObject:[NSNumber numberWithDouble:self.flySpeed] forKey:@"flySpeed"];
}
-(id)initWithCoder:(NSCoder *)aDecoder
{
//子类需要执行父类的解码方法
if(self = [super initWithCoder:aDecoder])
{
self.flyHeight = [[aDecoder decodeObjectForKey:@"flyHeight"] doubleValue];
self.flySpeed= [[aDecoder decodeObjectForKey:@"flySpeed"] doubleValue];
}
return self;
}