KVC & KVO

作者: wxhan | 来源:发表于2019-02-24 01:06 被阅读0次

一.什么是KVC

KVCkey-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中的每一个keyvalue,然后通过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

KVOkey-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文件重写nameset方法。在这一过程中,原先nameisa指针由原来的指向PersonKVO机制修改成指向了NSKVONotifying_Person

相关文章

网友评论

      本文标题:KVC & KVO

      本文链接:https://www.haomeiwen.com/subject/qebmyqtx.html