• 【原】iOS中KVC和KVO的区别


    在iOS开发中经常会看到KVC和KVO这两个概念,比较可能混淆,特地区分一下

    KVC(Key Value Coding)

     1> 概述

      KVC:Key Value Coding,键值编码,是一种间接访问实例变量的方法。

      KVC 提供了一个使用字符串(Key)而不是访问器方法,去访问一个对象实例变量的机制。

     2> KVC部分源码(头文件)

     1 // NSKeyValueCoding.h
     2 @interface NSObject(NSKeyValueCoding)
     3 
     4 + (BOOL)accessInstanceVariablesDirectly;
     5 
     6 - (nullable id)valueForKey:(NSString *)key;
     7 - (void)setValue:(nullable id)value forKey:(NSString *)key;
     8 - (BOOL)validateValue:(inout id __nullable * __nonnull)ioValue forKey:(NSString *)inKey error:(out NSError **)outError;
     9 
    10 - (NSMutableArray *)mutableArrayValueForKey:(NSString *)key;
    11 
    12 - (NSMutableOrderedSet *)mutableOrderedSetValueForKey:(NSString *)key NS_AVAILABLE(10_7, 5_0);
    13 
    14 - (NSMutableSet *)mutableSetValueForKey:(NSString *)key;
    15 
    16 - (nullable id)valueForKeyPath:(NSString *)keyPath;
    17 - (void)setValue:(nullable id)value forKeyPath:(NSString *)keyPath;
    18 - (BOOL)validateValue:(inout id __nullable * __nonnull)ioValue forKeyPath:(NSString *)inKeyPath error:(out NSError **)outError;
    19 - (NSMutableArray *)mutableArrayValueForKeyPath:(NSString *)keyPath;
    20 - (NSMutableOrderedSet *)mutableOrderedSetValueForKeyPath:(NSString *)keyPath NS_AVAILABLE(10_7, 5_0);
    21 - (NSMutableSet *)mutableSetValueForKeyPath:(NSString *)keyPath;
    22 
    23 - (nullable id)valueForUndefinedKey:(NSString *)key;
    24 - (void)setValue:(nullable id)value forUndefinedKey:(NSString *)key;
    25 - (void)setNilValueForKey:(NSString *)key;
    26 - (NSDictionary<NSString *, id> *)dictionaryWithValuesForKeys:(NSArray<NSString *> *)keys;
    27 - (void)setValuesForKeysWithDictionary:(NSDictionary<NSString *, id> *)keyedValues;
    28 
    29 @end
    30 
    31 @interface NSArray<ObjectType>(NSKeyValueCoding)
    32 
    33 - (id)valueForKey:(NSString *)key;
    34 - (void)setValue:(nullable id)value forKey:(NSString *)key;
    35 
    36 @end
    37 
    38 @interface NSDictionary<KeyType, ObjectType>(NSKeyValueCoding)
    39 
    40 - (nullable ObjectType)valueForKey:(NSString *)key;
    41 
    42 @end
    43 
    44 @interface NSMutableDictionary<KeyType, ObjectType>(NSKeyValueCoding)
    45 
    46 - (void)setValue:(nullable ObjectType)value forKey:(NSString *)key;
    47 
    48 @end
    49 
    50 @interface NSOrderedSet<ObjectType>(NSKeyValueCoding)
    51 
    52 - (id)valueForKey:(NSString *)key NS_AVAILABLE(10_7, 5_0);
    53 - (void)setValue:(nullable id)value forKey:(NSString *)key NS_AVAILABLE(10_7, 5_0);
    54 
    55 @end
    56 
    57 @interface NSSet<ObjectType>(NSKeyValueCoding)
    58 
    59 - (id)valueForKey:(NSString *)key;
    60 - (void)setValue:(nullable id)value forKey:(NSString *)key;
    61 
    62 @end

      可以看到这个类里面包含了对类NSObject、NSArray、NSDictionary、NSMutableDictionary、NSOrderedSet、NSSet的扩展,扩展方法基本上为

    - (id)valueForKey:(NSString *)key;
    - (void)setValue:(nullable id)value forKey:(NSString *)key;

    也就是说,基本上Objective-C里所有的对象都支持KVC操作,操作包含如上两类方法,动态读取和动态设值。

     3> 通过KVC键值编码访问属性

      ① key值查找

    1 [stu setValue:@"xiaoqiang" forKey:@"name"]; 
    2 [stu setValue:@"boy" forKey:@"gender"];
    3 [stu setValue:@24 forKey:@"age"]; 
    4 
    5 NSLog(@"name = %@, gender = %@, age = %@", [stu valueForKey:@"name"], [stu valueForKey:@"gender"], [stu valueForKey:@"age"]);

      ② 路径查找

    1 Teacher *tea = [[Teacher alloc] init];
    2 stu.teacher = tea;
    3 [stu setValue:@"fangfang" forKeyPath:@"teacher.name"];
    4 
    5 // 路径查找
    6 NSLog(@"teacherName = %@", [stu valueForKeyPath:@"teacher.name"]);

      ③ 同时给多个属性赋值

     1 NSDictionary *dict = @{
     2                        @"name" : @"fangfang",
     3                        @"gender" : @"girl",
     4                        @"age" : @18,
     5                        @"hobby" : @"fangfang"
     6                        };
     7 Student *stu2 = [[Student alloc] init];
     8 
     9 // 同时给多个属性赋值
    10 [stu2 setValuesForKeysWithDictionary:dict];
    11 
    12 NSLog(@"name = %@, gender = %@, age = %ld", stu2.name, stu2.gender, stu2.age);

     4> KVC抛出异常的方法

      ① 使用KVC设置值对象时

       如果当前类没有找到对象的Key值,系统会自动调用 setValue: forUndefinedKey: 方法

       该方法的默认实现是抛出一个异常,如果不想抛出异常,就重写这个方法

    1 // 重写
    2 // 使用KVC设置值对象
    3 - (void)setValue:(id)value forUndefinedKey:(NSString *)key
    4 {
    5     NSLog(@"不存在Key:%@", key);
    6 }

      ② 使用KVC取值的时候

         如果当前类没有找到对应的Key值,系统会自动调用 valueForUndefinedKey: 方法

         该方法的默认实现是抛出一个异常,如果不想抛出异常,就重写这个方法

    1 // 重写
    2 // 使用KVC取值的时候
    3 - (id)valueForUndefinedKey:(NSString *)key
    4 {
    5     return nil;
    6 }

     5> KVC的实现机制 

      KVC按顺序使用如下技术:

    • 检查是否存在getter方法-<key>或者setter方法-set<key>:的方法;        
    • 如果没有上述方法,则检查是否存在名字为-_<key><key>的实例变量;        
    • 如果仍未找到,则调用 valueForUndefinedKey:setValue: forUndefinedKey: 方法。这些方法的默认实现都是抛出异常,我们可以根据需要重写它们。

    KVO(Key Value Observer)

     1> 概述

      KVO:(Key Value Observer)键值观察者,是观察者设计模式的一种具体实现

      KVO触发机制:一个对象(观察者),监测另一对象(被观察者)的某属性是否发生变化,若被监测的属性发生的更改,会触发观察者的一个方法(方法名固定,类似代理方法)

     2> 一部分NSKeyValueObserving.h对于NSObject的拓展代码

     1 @interface NSObject(NSKeyValueObserving)
     2 
     3 - (void)observeValueForKeyPath:(nullable NSString *)keyPath ofObject:(nullable id)object change:(nullable NSDictionary<NSString*, id> *)change context:(nullable void *)context;
     4 
     5 @end
     6 
     7 @interface NSObject(NSKeyValueObserverRegistration)
     8 
     9 - (void)addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(nullable void *)context;
    10 - (void)removeObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath context:(nullable void *)context NS_AVAILABLE(10_7, 5_0);
    11 - (void)removeObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath;
    12 
    13 @end

      从拓展名称就可以看出,使用KVO需要注册监听器,也需要删除监听器。监听过程需要使用observeValueForKeyPath回调方法。

     3> 使用步骤

      注册观察者(为被观察者指定观察者以及被观察属性)

     1 // KVO 键值观察,是观察者设计模式
     2 @interface ViewController ()
     3 
     4 // 观察可变数组的改变情况(苹果官方文档不建议对数组进行观察)
     5 @property (nonatomic, strong) NSMutableArray *array;
     6 
     7 @end
     8 
     9 - (void)viewDidLoad {
    10     [super viewDidLoad];
    11     // Do any additional setup after loading the view, typically from a nib.
    12     
    13     self.array = [NSMutableArray array];
    14     
    15     // 第一步:注册观察者
    16     // 参数1:添加的观察者对象
    17     // 参数2:字符串标识的key
    18     // 参数3:触发添加观察者对象的时候
    19     /*
    20      NSKeyValueObservingOptionNew = 0x01 key或value只要有一个更新的时候就会触发
    21      NSKeyValueObservingOptionOld = 0x02
    22      NSKeyValueObservingOptionInitial = 0x04
    23      NSKeyValueObservingOptionPrior = 0x08
    24      */
    25     // 参数4:文本内容 一般为nil
    26     [self addObserver:self forKeyPath:@"array" options:NSKeyValueObservingOptionNew context:nil];
    27 }

      
实现回调方法

     1 // 第二步:实现回调
     2 - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context
     3 {
     4     
     5     NSLog(@"keyPath = %@", keyPath);
     6     NSLog(@"object = %@", object);
     7     NSLog(@"change = %@", change);
     8     
     9     // 可以进行刷新UI的操作
    10 }

      触发回调方法(被观察属性发生更改)

     1 // 第二步:实现回调
     2 - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context
     3 {
     4     
     5     NSLog(@"keyPath = %@", keyPath);
     6     NSLog(@"object = %@", object);
     7     NSLog(@"change = %@", change);
     8     
     9     // 可以进行刷新UI的操作
    10 }

      移除观察者

       在不需要观察者的时候需要把它删除,本人就只在视图将要消失时移除

    1 // 视图将要消失的时候
    2 - (void)viewWillDisappear:(BOOL)animated
    3 {
    4     // 在不需要观察者的时候需要把它删除
    5     [self removeObserver:self forKeyPath:@"array"];
    6 }
  • 相关阅读:
    使用Linq 更新数据库时遇到的一些问题及解决办法
    Asp.net 初级面试(转)
    jQuery设计思想
    WinCE BSP的BIB文件介绍
    团队管理经验(转)
    35岁以前成功的9大好习惯
    早起的十个好处,以及如何做到早起
    男人魅力的九个方面
    SQL优化34条(转)
    深入理解Javascript闭包(closure)
  • 原文地址:https://www.cnblogs.com/gfxxbk/p/5487607.html
Copyright © 2020-2023  润新知