一.什么是KVC
KVC,key-value-coding,键值编码,即通过 key(属性名称)对 value(属性值)进行 coding(编码,即取值或赋值)。不但可以简化代码,还可以访问私有属性。
二.KVC实现字典转模型
#import <Foundation/Foundation.h>
@interface Teacher : NSObject
@property (nonatomic,copy)NSString *name;
@property (nonatomic,copy)NSString *age;
+ (instancetype)modelWithDic:(NSDictionary *)dic;
- (instancetype)initWithDic:(NSDictionary *)dic;
@end
#import "Teacher.h"
@implementation Teacher
+ (instancetype)modelWithDic:(NSDictionary *)dic{
return [[Teacher alloc] initWithDic:dic];
}
- (instancetype)initWithDic:(NSDictionary *)dic{
if (self = [super init]) {
[self setValuesForKeysWithDictionary:dic];
}
return self;
}
- (void)setValue:(id)value forUndefinedKey:(NSString *)key{}
@end
[self setValuesForKeysWithDictionary:dic]内部实现原理如下
遍历dict中的每一个key和value,然后通过setValue:forKey:进行赋值
[dict enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
[Teacher setValue:obj forKey:key];
}];
在这个例子中[Teacher setValue:obj forKey:key];其实就是
[Teacher setValue:dict[@"name"] forKey:@"name"];
[Teacher setValue:dict[@"age"] forKey:@"age"];
使用setValue:forKey:赋值的逻辑是:拿name举例
1.在模型中查找是否有setName方法。即(key的setter方法),如果有,赋值[self set:dict[@"name"]];。
2.如果1中没找到,查找模型中有没有_name属性,如果有,赋值_name = dict[@"name"];
3.如果2中没找到,查找模型中有没有name属性,如果有,赋值name = dict[@"name"];
4.如果还找不到,会报错[<Teacher 0x600002276e60> setValue:forUndefinedKey:]解决这一报错是重写- (void)setValue:(id)value forUndefinedKey:(NSString *)key{}
三.什么是KVO
KVO,key-value-observe,键值监听,即对某个类的属性进行监听,当这个属性发生变化时,触发监听方法。
举个例子:监听Person里面的属性name
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface Person : NSObject
@property (nonatomic,copy)NSString *name;
@end
NS_ASSUME_NONNULL_END
- 方法一: 重写
name的set方法
#import "Person.h"
@implementation Person
- (void)setName:(NSString *)name{
_name = name;
NSLog(@"新值:%@",name);
}
@end
#import "ViewController.h"
#import "Person.h"
@interface ViewController ()
@property (nonatomic,strong)Person *p;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
_p = [[Person alloc] init];
_p.name = @"wxh";
}
- 方法二: 使用
KVO
#import "ViewController.h"
#import "Person.h"
@interface ViewController ()
@property (nonatomic,strong)Person *p;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
_p = [[Person alloc] init];
[_p addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew context:nil];
_p.name = @"wxh";
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context{
if ([keyPath isEqualToString:@"name"]) {
NSLog(@"新值:%@",[change valueForKey:NSKeyValueChangeNewKey]);
}
}
- (void)dealloc {
[_p removeObserver:self forKeyPath:@"name"];
}
@end
打印结果都为新值:wxh
KVO实现原理:跟重写set方法差不多意思,KVO机制是通过runtime机制实现的,当对属性name进行监听时,KVO会动态创建Person的子类(类名为NSKVONotifying_Person)。也就是说NSKVONotifying_Person是继承于Person的·。然后在NSKVONotifying_Person.m文件重写name的set方法。在这一过程中,原先name的isa指针由原来的指向Person被KVO机制修改成指向了NSKVONotifying_Person。