• iOS开发中KVC、KVO简介


    在iOS开发中,KVC和KVO是经常被用到的。可以使用KVC对对象的属性赋值和取得对象的属性值,可以使用KVO监听对象属性值的变化。简单介绍一下KVC和KVO。

    一:键值编码(KVC)

    KVC,全称 Key Value Coding(键值编码),是OC 语言的一个特性,使用KVC,可以对对象的属性进行动态读写。

    KVC的操作方法由 NSKeyValueCoding协议提供,而NSObject已经实现了这个协议,因此OC中的几乎所有对象都可以使用KVC操作。常用的KVC操作方法有:

    (1)设置属性值

      setValue:value forKey:key (用于简单路径,也就是直接属性)

      setValue: value forKeyPath:key (用于复杂路径,也就是间接属性)

    (2)获取属性值

      valueForKey: key  (用于获取简单路径的属性值)

      valueForKeyPath: key (用于获取复杂路径的属性值)

    通过代码来看KVC的具体使用:

    两个类分别是Dog类和Person类

    Dog.h

    复制代码
    1 #import <Foundation/Foundation.h>
    2 
    3 @interface Dog : NSObject
    4 
    5 @property (nonatomic, copy) NSString *name;
    6 
    7 @end
    复制代码

    Dog.m

    1 #import "Dog.h"
    2 
    3 @implementation Dog
    4 
    5 @end

    Person.h

    复制代码
     1 #import <Foundation/Foundation.h>
     2 
     3 @class Dog;
     4 
     5 @interface Person : NSObject
     6 
     7 @property (nonatomic, copy) NSString *name;
     8 @property (nonatomic, copy) NSString *sex;
     9 
    10 @property (nonatomic, strong) Dog *dog;
    11 
    12 @end
    复制代码

    Person.m

    1 #import "Person.h"
    2 
    3 @implementation Person
    4 
    5 @end

    KVC的使用:

    复制代码
     1 Person *person = [[Person alloc] init];
     2     //kvc  设置值
     3     [person setValue:@"Jim" forKey:@"name"];
     4     [person setValue:@"boy" forKey:@"sex"];
     5     
     6     //kvc 获得值
     7     NSLog(@"name = %@  sex = %@",[person valueForKey:@"name"],[person valueForKey:@"sex"]);
     8     
     9     Dog *dog = [[Dog alloc] init];
    10     person.dog = dog;
    11     //kvc  设置复杂路径值
    12     [person setValue:@"Teddy" forKeyPath:@"dog.name"];
    13     //kvc  获得复杂路径值
    14     NSLog(@"dog name = %@",[person valueForKeyPath:@"dog.name"]);
    复制代码

    可以看到,KVC使用对应的函数即可设置值、获得值。那么,KVC是如何查找一个属性进行读取呢?KVC遵循下面的规则,假设要对name属性进行读取:

    (1)设置属性:优先考虑setName方法;如果没有,则搜索成员变量_name;如果没找到,则搜索成员变量name;如果还没有找到,则调用 setValue: forUndefineKey: 方法。

    (2)读取属性:优先考虑getName方法;如果没有,则搜索成员变量_name;如果没找到,则搜索成员变量name;如果还没有找到,则调用 valueForUndefineKey: 方法。

    二:KVC中 setValuesForKeysWithDictionary: 的使用

    KVC中有一个非常重要的方法: setValuesForKeysWithDictionary:dict ,该方法可以将一个字典映射到一个对象,省去了给对象一一赋值的步骤。

    使用 setValuesForKeysWithDictionary:dict 的一个例子:

    student.h

    复制代码
     1 #import <Foundation/Foundation.h>
     2 
     3 @interface Student : NSObject
     4 
     5 /**
     6  *  学号
     7  */
     8 @property (nonatomic, copy) NSString *num;
     9 /**
    10  *  姓名
    11  */
    12 @property (nonatomic, copy) NSString *name;
    13 /**
    14  *  身高
    15  */
    16 @property (nonatomic, assign) float height;
    17 
    18 /**
    19  *  初始化的两个方法
    20  *
    21  */
    22 - (instancetype)initWithDict:(NSDictionary *)dict;
    23 + (instancetype)stuWithDict:(NSDictionary *)dict;
    24 
    25 
    27 
    28 @end
    复制代码

    student.m

    复制代码
     1 - (instancetype)initWithDict:(NSDictionary *)dict{
     2     if(self = [super init]){
     3         [self setValuesForKeysWithDictionary:dict];
     4     }
     5     return self;
     6 }
     7 
     8 + (instancetype)stuWithDict:(NSDictionary *)dict
     9 {
    10     return [[self alloc] initWithDict:dict];
    11 }
    12 
    复制代码

    用一个字典初始化对象

    复制代码
     1 - (void)initStudent
     2 {
     3     NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:
     4     @"Tom",@"name",
     5     @"110",@"num",
     6     @"170.0",@"height",
     7     nil];
     8     Student *stu = [[Student alloc] initWithDict:dict];
     9     NSLog(@"name = %@ num = %@ height = %f",stu.name,stu.num,stu.height);
    10 }
    复制代码

    setValuesForKeyWithDictionary:dict  的原理

      实际上,setValuesForKeyWithDictionary:dict 方法就是遍历dict,对dict 中的每个键值调用 setValue: forKey: 方法。可以用下面的方法模拟setValuesForKeyWithDictionary:dict:

    复制代码
    1 - (void) setProperty:(NSDictionary *)dict
    2 {
    3     for(NSString *key in [dict allKeys])
    4     {
    5         NSString *value = [dict objectForKey:key];
    6         [self setValue:value forKey:key];
    7     }
    8 }
    复制代码

    调用时:

    复制代码
    1 - (instancetype)initWithDict:(NSDictionary *)dict{
    2     if(self = [super init]){
    3         //[self setValuesForKeysWithDictionary:dict];
    4         [self setProperty:dict];
    5     }
    6     return self;
    7 }
    复制代码

    和setValuesForKeyWithDictionary:dict 功能是一样的。

    使用setValuesForKeyWithDictionary:dict一个需要注意的地方

    当字典中有某个值,而对象没有相应的属性时,会发生崩溃。比如,新的字典如下:

    复制代码
     1 - (void)initStudent
     2 {
     3     NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:
     4     @"Tom",@"name",
     5     @"110",@"num",
     6     @"170.0",@"height",
     7     @"boy",@"sex",nil];
     8     Student *stu = [[Student alloc] initWithDict:dict];
     9     NSLog(@"name = %@ num = %@ height = %f",stu.name,stu.num,stu.height);
    10 }
    复制代码

    字典中有 sex属性,但是Studeng对象中没有这个属性,此时会发生崩溃。解决方法:

    实现 setValue:  forUndefineKey:  方法,在该方法中处理出现没有属性的情况。

    student.h中添加代码:

    1 - (void)setValue:(id)value forUndefinedKey:(NSString *)key;

    student.m中添加代码:

    1 - (void)setValue:(id)value forUndefinedKey:(NSString *)key
    2 {
    3     
    4 }

    当出现没有属性的情况时,就会调用 setValue: forUndefineKey: 方法。因为该方法没做处理,所以这种情况不做处理,不会发生崩溃。需要注意的是:setValue: forUndefineKey: 方法用途很广泛,比如说字典中某个key值 为id,但是在OC中id 是关键字,这种情况也可以在 setValue: forUndefineKey: 方法中处理。

    三:键值监听(KVO)

    KVO全称 Key Value Observing。使用KVO可以实现视图组件和数据模型的分离,视图作为监听器,当模型的属性值发生变化后,监听器可以做相应的处理。KVO的方法由NSKeyValueObserving协议提供,同样NSObject已经实现了该协议,因此几乎所有的对象都可以使用KVO。

    使用KVO操作常用的方法如下:

    注册制定路径的监听器: addObserver:  forKeyPath: option: context:

    删除制定路径的监听器:removeObserver: forKeyPath:

    触发监听时的方法:observeValueForKeyPath: ofObject: change: context: 

    一个KVO的例子:

    有两个类: Dog类和People类

    Dog.h

    复制代码
    1 #import <Foundation/Foundation.h>
    2 
    3 @interface Dog : NSObject
    4 
    5 @property (nonatomic, copy) NSString *name;
    6 
    7 @end
    复制代码

    Dog.m

    1 #import "Dog.h"
    2 
    3 @implementation Dog
    4 
    5 @end

    People.h

    复制代码
     1 #import <Foundation/Foundation.h>
     2 
     3 @class Dog;
     4 
     5 @interface People : NSObject
     6 
     7 @property (nonatomic , copy) NSString *name;
     8 @property (nonatomic , strong) Dog *dog;
     9 
    10 @end
    复制代码

    People.m

    复制代码
     1 #import "People.h"
     2 #import "Dog.h"
     3 
     4 @implementation People
     5 
     6 /**
     7  *  初始化时增加对dog的监听
     8  *
     9  */
    10 - (void)setDog:(Dog *)dog
    11 {
    12     _dog = dog;
    13     [self.dog addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew context:nil];
    14 }
    15 /**
    16  *  重写observeValueForKeyPath方法
    17  */
    18 - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context
    19 {
    20     if([keyPath isEqualToString:@"name"]){
    21         NSLog(@"newName = %@",[change objectForKey:@"new"]);
    22     }
    23 }
    24 
    25 - (void)dealloc
    26 {
    27     //移除监听
    28     [self.dog removeObserver:self forKeyPath:@"name"];
    29 }
    30 
    31 @end
    复制代码

    测试KVO函数:

    复制代码
     1 - (void)testKVO
     2 {
     3     People *people = [[People alloc] init];
     4     people.name = @"Tom";
     5     Dog *dog = [[Dog alloc] init];
     6     dog.name = @"Teddy";
     7     people.dog = dog;
     8     
     9     //执行到这一步后,会触发监听器的函数(observeValueForKeyPath)
    10     dog.name = @"testChange";
    11 }
    复制代码

    在代码中,当修改dog 的name属性时,就会触发监听方法,也就是 

    observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context 方法。
  • 相关阅读:
    python 小数和百分数之间的转换
    Pandas datetime常用属性
    Pandas 时间处理 获取数组中某月的数据
    Pandas如何将两个Series对象进行与运算
    【数据结构与算法】数组的增删改查
    【效率为王】超详细 Hexo + Github Pages 博客搭建教程
    大牛们都是怎么玩 Manjora 的
    利用 Halo 从 0 到 1 搭建属于自己的博客
    十分钟入门 Markdown 写作
    当前知识水平下的问题复杂度分类
  • 原文地址:https://www.cnblogs.com/yulei126/p/6790253.html
Copyright © 2020-2023  润新知