KVC & KVO
一.什么是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
。