KVC & KVO

作者: 秋月夜 | 来源:发表于2016-02-26 00:17 被阅读324次

KVC - Key Value Coding 键值编码

一、KVC 简介

KVC 是一种可以直接通过字符串 key 来访问类属性的机制。而不是通过调用 Setter、Getter 方法访问。

二、Set

1.单个值 setValue:forkey:

// 只能给对象的直接属性赋值
// 结果等同 p.name = @"Nil Chou";
[p setValue:@"Nil Chou" forKey:@"name"];

2.多层赋值 setValue:forKeyPath 可以给对象的间接属性赋值

// 建议: 以后在开发中都使用setValue:forKeyPath:
[p setValue:@"Xiao Bai" forKeyPath:@"dog.name"];

3.给私有成员变量赋值

[p setValue:@"Nil Chou" forKey:@"_myName"]; 
[p setValue:@"Nil Chou" forKeyPath:@"_myName"]; // 推荐

4.字典转模型 - Notice

  • 如果想使用 KVC 进行字典转模型,那么字典中的 key 必须和 Model 中的属性一模一样(个数 + 名称)。
    否则报经典错误:this class is not key value coding-compliant for the key score.
    处理方式:在 Model.m 中添加 - (void)setValue:(id)value forUndefinedKey:(NSString *)key{} 防止程序崩溃。
  • 如果使用 KVC 进行字典转模型,只能对当前调用 KVC 方法的对象进行转换,不能对它的属性的对象进行转换,意思就是说:对象中的对象无法转换,需要另行进行手动转换,再次处理。- (instancetype)initWithDict:(NSDictionary *)dict{}

5.setValuesForKeysWithDictionary:方法内部的实现原理

// 1.会拿到字典中的 key,然后根据这个 key 取出字典中的值,然后再根据这个 key 赋值给对象 
// [p setValue:@"xxx" forKey:@"name"];
[p setValuesForKeysWithDictionary:dict];

三、Get

1.获取单个值
NSString *name = [p valueForKey:@"name"];

2.获取多层值
NSString *dogName = [p valueForKeyPath:@"dog.name"];

3.模型转字典
NSDictionary *dict = [p dictionaryWithValuesForKeys:@[@"name", @"money"]];

4.获取数组中对象的值

Person *p1 = [Person new]; 
p1.name = @"Zhang San"; 
p1.money = 1111; 

Person *p2 = [Person new]; 
p2.name = @"Li Si"; 
p2.money = 2222; 

NSArray *arr = @[p1, p2]; 
// 如果数组中的元素都是同一种类型的数据, 可以使用KVC获取数组中所有对象的某个属性的值
NSArray *res = [arr valueForKeyPath:@"name"]; 
NSLog(@"res = %@", res); 
// 获取去数组中name属性的所有值:Zhang San, Li Si

5.运算符

id res1 = [arr valueForKeyPath:@"@avg.money"]; 
// @avg.money:求出money的平均值 
NSLog(@"res = %@", res1);
```

##四、Articles
- [KVC & KVO](http://www.jianshu.com/p/d5974566fd2f)
- [KVC(iOS)](http://www.jianshu.com/p/099f7af0e67a)
- [KVC](http://www.jianshu.com/p/099f7af0e67a)

#KVO - Key Value Observing 键值观察
##一、KVO 简介
KVO 是基于 runtime 机制实现的。当某个类的属性对象第一次被观察时,系统就会在运行期间动态地创建该类的一个派生类,在这个派生类中重写基类的任何被观察属性的 setter 方法。派生类在被重写的 setter 方法内实现真正的通知机制。使用 KVO,可以方便地对指定对象的某个属性进行观察,当属性发生变化时,进行通知。

##二、KVO 三部曲
**1. 添加观察者 **
为了正确接受属性的变化通知,观察者对象必须先发一个消息给被观察者对象。
```
/**
 *  由被观察者调用方法,添加观察者
 *
 *  @param observer 观察者
 *  @param keyPath  观察哪一个属性
 *  @param options  设定通知观察者时传递的属性值,改变后:NSKeyValueObservingOptionNew,改变前:NSKeyValueObservingOptionOld
 *  @param context  一些其他的需要传递给观察者的上下文信息,通常设置为nil
 */
- (void)addObserver:(NSObject *)observer
         forKeyPath:(NSString *)keyPath
            options:(NSKeyValueObservingOptions)options
            context:(void *)context;
```
**2. 接受变化通知**
应该注意的是如果只是使用成员变量改变值的话是不会触发KVO的。要使用点语法,或者是KVC的方式改变值。
```
/**
 *  一旦被观察的属性发生变化,系统就会调用这个方法,必须要实现的方法
 *
 *  @param keyPath 监控的属性
 *  @param object  是我们 监听的对象
 *  @param change  里面包含了 keyPath 对应的新增
 *  @param context 一些其他的需要传递给观察者的上下文信息,通常设置为nil
 */
- (void)observeValueForKeyPath:(NSString *)keyPath
                      ofObject:(id)object
                        change:(NSDictionary<NSString *,id> *)change
                       context:(void *)context;
```
**3. 移除观察者**
在不需要观察时要进行移除,需要注意的是,在 ARC 和 MRC 下都要移除观察者,在 ARC 下不调用```[super dealloc]``` 。
```
/**
 *  移除观察者
 *
 *  @param observer 观察者
 *  @param keyPath  监控的属性
 */
- (void)removeObserver:(NSObject *)observer
            forKeyPath:(NSString *)keyPath;
```

##三、Example
**Code**
```
- (void)viewDidLoad {
    [super viewDidLoad];
    
    Person *p = [[Person alloc] init];
    p.name = @"Nil Chou";
    p.age = @18;

    // 1.
    [p addObserver:self
        forKeyPath:@"age"
           options:NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew
           context:nil];
    p.age = @24;
    
    // 3.
    [p removeObserver:self forKeyPath:@"age" context:nil];
}

// 2.
- (void)observeValueForKeyPath:(NSString *)keyPath
                      ofObject:(id)object
                        change:(NSDictionary<NSString *,id> *)change
                       context:(void *)context {
    
    NSLog(@"keyPath = %@, object = %@ , change = %@, context = %@", keyPath, object, change, context);
    // 对比 change 字典中 NSKeyValueChangeOldKey 与 NSKeyValueChangeNewKey,可判断监听属性的变化
    NSNumber *oldAge = change[NSKeyValueChangeOldKey];
    NSNumber *newAge = change[NSKeyValueChangeNewKey];
    NSLog(@"oldAge = %@, newAge = %@",oldAge, newAge);
    
}
```
**Console**
```
2016-02-25 21:58:26.355 KVO[14298:3724330] keyPath = age, object = <Person: 0x7fe493695780> , change = {
    kind = 1;
    new = 24;
    old = 18;
}, context = (null)
2016-02-25 21:58:26.355 KVO[14298:3724330] oldAge = 18, newAge = 24
```

##四、KVO 优劣
- 主要优点:每一次属性值改变都是自动发送通知,不需要开发者手动实现。
- 主要缺点:只能复写``` -observeValueForKeyPath:ofObject:change:context:``` 方法来获得通知,不能自定义 selector 来获取。也不能传递 block 等。

##五、Articles
- [KVO介绍](http://www.jianshu.com/p/0f3554e37141)
- [KVO(iOS)](http://www.jianshu.com/p/ed0422ffeeda)
- [KVO/NSNotification](http://www.jianshu.com/p/65ea732770eb)
- [Key-Value Observing](http://nshipster.cn/key-value-observing/)
- [iOS开发-KVO的奥秘](http://www.jianshu.com/p/742b4b248da9)

相关文章

  • KVC && KVO

    1.KVC 关于KVC和KVO,我之前的总结文章有写过,但是实际上我在平日工作里,KVC和KVO使用的相对较少,不...

  • KVO & KVC

    一、KVO 二、KVCkey value coding : 间接通过字符串类型的key取出对应的属性值

  • KVO & KVC

    KVO: KVO的全称是Key-Value Observing,俗称“键值监听”,可以用于监听某个对象属性值的改变...

  • KVC & KVO

    KVC(Key-Value Coding) KVC最常见的两个用途: 给私有的成员变量赋值(当然公有的也可以)比如...

  • KVC & KVO

    平时用 KVO 用的少啊, 基本上都是不用的那种, 以至于怎么用都不知道了。晚上下班回来,还是决定要恶补啊,毕竟看...

  • KVC & KVO

    KVC - Key Value Coding 键值编码 一、KVC 简介 KVC 是一种可以直接通过字符串 key...

  • KVC、KVO

    KVC、KVO探识(一)KVO和KVO的详细使用 KVC、KVO探识(二)KVC你不知道的东西 KVC、KVO探识...

  • KVC,KVO

    KVC , KVO KVC和KVO的区别及应用 KVC/KVO原理 1. KVC键值编码 KVC,即是指NSKey...

  • KVC KVO高阶应用

    KVC KVO高阶应用 KVC KVO高阶应用

  • GNUstep KVC/KVO探索(二):KVO的内部实现

    GNUstep KVC/KVO探索(一):KVC的内部实现GNUstep KVC/KVO探索(二):KVO的内部实...

网友评论

      本文标题:KVC & KVO

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