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

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