您的位置:首页 > 移动开发 > IOS开发

iOS中KVC和KVO的区别

2016-05-12 22:15 369 查看
在iOS开发中经常会看到KVC和KVO这两个概念,比较可能混淆,特地区分一下

KVC(Key Value Coding)

 1> 概述

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

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

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

// NSKeyValueCoding.h
@interface NSObject(NSKeyValueCoding)

+ (BOOL)accessInstanceVariablesDirectly;

- (nullable id)valueForKey:(NSString *)key;
- (void)setValue:(nullable id)value forKey:(NSString *)key;
- (BOOL)validateValue:(inout id __nullable * __nonnull)ioValue forKey:(NSString *)inKey error:(out NSError **)outError;

- (NSMutableArray *)mutableArrayValueForKey:(NSString *)key;

- (NSMutableOrderedSet *)mutableOrderedSetValueForKey:(NSString *)key NS_AVAILABLE(10_7, 5_0);

- (NSMutableSet *)mutableSetValueForKey:(NSString *)key;

- (nullable id)valueForKeyPath:(NSString *)keyPath;
- (void)setValue:(nullable id)value forKeyPath:(NSString *)keyPath;
- (BOOL)validateValue:(inout id __nullable * __nonnull)ioValue forKeyPath:(NSString *)inKeyPath error:(out NSError **)outError;
- (NSMutableArray *)mutableArrayValueForKeyPath:(NSString *)keyPath;
- (NSMutableOrderedSet *)mutableOrderedSetValueForKeyPath:(NSString *)keyPath NS_AVAILABLE(10_7, 5_0);
- (NSMutableSet *)mutableSetValueForKeyPath:(NSString *)keyPath;

- (nullable id)valueForUndefinedKey:(NSString *)key;
- (void)setValue:(nullable id)value forUndefinedKey:(NSString *)key;
- (void)setNilValueForKey:(NSString *)key;
- (NSDictionary<NSString *, id> *)dictionaryWithValuesForKeys:(NSArray<NSString *> *)keys;
- (void)setValuesForKeysWithDictionary:(NSDictionary<NSString *, id> *)keyedValues;

@end

@interface NSArray<ObjectType>(NSKeyValueCoding)

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

@end

@interface NSDictionary<KeyType, ObjectType>(NSKeyValueCoding)

- (nullable ObjectType)valueForKey:(NSString *)key;

@end

@interface NSMutableDictionary<KeyType, ObjectType>(NSKeyValueCoding)

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

@end

@interface NSOrderedSet<ObjectType>(NSKeyValueCoding)

- (id)valueForKey:(NSString *)key NS_AVAILABLE(10_7, 5_0);
- (void)setValue:(nullable id)value forKey:(NSString *)key NS_AVAILABLE(10_7, 5_0);

@end

@interface NSSet<ObjectType>(NSKeyValueCoding)

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

@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值查找

[stu setValue:@"xiaoqiang" forKey:@"name"];
[stu setValue:@"boy" forKey:@"gender"];
[stu setValue:@24 forKey:@"age"];

NSLog(@"name = %@, gender = %@, age = %@", [stu valueForKey:@"name"], [stu valueForKey:@"gender"], [stu valueForKey:@"age"]);


  ② 路径查找

Teacher *tea = [[Teacher alloc] init];
stu.teacher = tea;
[stu setValue:@"fangfang" forKeyPath:@"teacher.name"];

// 路径查找
NSLog(@"teacherName = %@", [stu valueForKeyPath:@"teacher.name"]);


  ③ 同时给多个属性赋值

NSDictionary *dict = @{
@"name" : @"fangfang",
@"gender" : @"girl",
@"age" : @18,
@"hobby" : @"fangfang"
};
Student *stu2 = [[Student alloc] init];

// 同时给多个属性赋值
[stu2 setValuesForKeysWithDictionary:dict];

NSLog(@"name = %@, gender = %@, age = %ld", stu2.name, stu2.gender, stu2.age);


 4> KVC抛出异常的方法

  ① 使用KVC设置值对象时

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

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

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


  ② 使用KVC取值的时候

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

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

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


 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的拓展代码

@interface NSObject(NSKeyValueObserving)

- (void)observeValueForKeyPath:(nullable NSString *)keyPath ofObject:(nullable id)object change:(nullable NSDictionary<NSString*, id> *)change context:(nullable void *)context;

@end

@interface NSObject(NSKeyValueObserverRegistration)

- (void)addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(nullable void *)context;
- (void)removeObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath context:(nullable void *)context NS_AVAILABLE(10_7, 5_0);
- (void)removeObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath;

@end


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

 3> 使用步骤

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

// KVO 键值观察,是观察者设计模式
@interface ViewController ()

// 观察可变数组的改变情况(苹果官方文档不建议对数组进行观察)
@property (nonatomic, strong) NSMutableArray *array;

@end

- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.

self.array = [NSMutableArray array];

// 第一步:注册观察者
// 参数1:添加的观察者对象
// 参数2:字符串标识的key
// 参数3:触发添加观察者对象的时候
/*
NSKeyValueObservingOptionNew = 0x01 key或value只要有一个更新的时候就会触发
NSKeyValueObservingOptionOld = 0x02
NSKeyValueObservingOptionInitial = 0x04
NSKeyValueObservingOptionPrior = 0x08
*/
// 参数4:文本内容 一般为nil
[self addObserver:self forKeyPath:@"array" options:NSKeyValueObservingOptionNew context:nil];
}


  
实现回调方法

// 第二步:实现回调
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context
{

NSLog(@"keyPath = %@", keyPath);
NSLog(@"object = %@", object);
NSLog(@"change = %@", change);

// 可以进行刷新UI的操作
}


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

// 第二步:实现回调
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context
{

NSLog(@"keyPath = %@", keyPath);
NSLog(@"object = %@", object);
NSLog(@"change = %@", change);

// 可以进行刷新UI的操作
}


  移除观察者

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

// 视图将要消失的时候
- (void)viewWillDisappear:(BOOL)animated
{
// 在不需要观察者的时候需要把它删除
[self removeObserver:self forKeyPath:@"array"];
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: