KVC / KVO

KVC

  • Full Name: Key Value Coding (coding key)
  • Assignment
      
      
1
2
3
4
      
      
- ( void)setValue:( id)value forKey:( NSString *)key;
- ( void)setValue:( id)value forKeyPath:( NSString *)keyPath;
- ( void)setValuesForKeysWithDictionary:( NSDictionary *)keyedValues;
  • The value
      
      
1
2
3
4
      
      
// can get the value of a private member variables
- ( id)valueForKey:( NSString *)key;
- ( id)valueForKeyPath:( NSString *)keyPath;
- ( NSDictionary *)dictionaryWithValuesForKeys:( NSArray *)keys;
  • KVC underlying implementation
      
      
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
      
      
+ ( instancetype)flagWithDict:( NSDictionary *)dict
{
Flag *flag = [[ self alloc] init];
// KVC the dictionary property value into a model property value
// 如果使用KVC字典转模型,一定要保证模型的属性名跟字典里的key要一一对应
[flag setValuesForKeysWithDictionary:dict];
// 1.遍历字典里面所有的key和值,name,icon
// enumerateKeysAndObjectsUsingBlock:遍历字典中的所有keys和valus
// [dict enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
// NSLog(@"%@ %@",key,obj);
//
// // 利用KVC给模型中属性赋值,,
// // key:用来给哪个属性
// // Value:给模型的值
// [flag setValue:obj forKey:key];
//
// }];
// 2.[flag setValue:dict[@"name"] forKey:@"name"];
// [flag setName:dict[@"name"]] _name = dict[@"name"
// flag.icon = [UIImage imageNamed:dict[@"icon"]];
return flag;
}
// [flag setValue:obj forKey:key];
// [flag setValue:dict[@"icon"] forKey:@"icon"];
// setValue:forKey:方法:给模型的属性赋值
// 如何赋值
// 1.就会去模型中查找有没有setIcon方法,就直接调用这个set方法,给模型这个属性赋值[self setIcon:dict[@"icon"]]
// 2.接着就会去寻找有没有icon属性,如果有,就直接访问模型中icon = dict[@"icon"]
// 3.接着又会去寻找_icon属性,如果有,直接_icon = dict[@"icon"]
// 4.如果都找不到就会报错,[<Flag 0x7fb74bc7a2c0> setValue:forUndefinedKey:]
- ( void)setIcon:( NSString *)icon
{
_icon = [ UIImage imageNamed:icon];
NSLog( @"%@", [icon class]);
}

KVO

  • 全称:Key Value Observing(键值监听)
  • 作用:监听模型的属性值改变
  • 步骤
    • 添加监听器
  • KVO是一个观察者模式。观察一个对象的属性,注册一个指定的路径,若这个对象的的属性修改,则KVO会自动通知观察者。
      
      
1
2
3
4
5
6
7
8
9
10
11
      
      
// 利用b对象来监听a对象name属性的改变
/**
* 为对象p添加一个观察者(监听器)
*
* @param Observer:观察者(监听器)
* @param KeyPath:属性名(需要监听哪个属性
*/
[a addObserver:b forKeyPath: @"name" options: NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew context: @"test"];
/** 移除监听器 */
[a removeObserver:b forKeyPath: @"name"];
- 在监听器中实现监听方法
      
      
1
2
3
4
5
6
7
8
9
10
11
12
      
      
/**
* 当利用KVO监听到某个对象的属性值发生了改变,就会自动调用这个
*
* @param keyPath 哪个属性被改了
* @param object 哪个对象的属性被改了
* @param change 改成咋样
* @param context 当初addObserver时的context参数值
*/
-( void)observeValueForKeyPath:( NSString *)keyPath ofObject:( id)object change:( NSDictionary *)change context:( void *)context
{
NSLog( @"%@ %@ %@ %@", object, keyPath, change, context);
}
  • KVO的观察者是由两种模式的。一种是自动通知,一种是手动通知。

    • 自动通知自动监听对象的属性,不管这个属性的前后属性变化的值是否一样,都会通知观察者。

    • 手动通知重写willChangeValueForKey:和didChangeValueForKey: 方法通知观察者。

    • 一般都是用自动通知,方便快捷。
      下面两种写法,都会举例说明。

  • 自动通知,主要看监听回调的分析
    场景:模拟人年龄的变化,看接受通知的次数

      
      
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
      
      
- ( void)viewDidLoad {
[ super viewDidLoad];
self.person = [[Person alloc]init];
self.person.age = 15;
/**
* 注册监听
*/
[ self.person addObserver: self forKeyPath: @"age" options: NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context: nil];
/**
* 第二次赋值 20
*/
self.person.age = 20;
/**
* 再次赋值 20
*/
self.person.age = 20;
/**
* 详细见控制台输出,只比较新旧值
*/
}
/**
* 监听回调
*/
-( void)observeValueForKeyPath:( NSString *)keyPath ofObject:( id)object change:( NSDictionary< NSString *, id> *)change context:( void *)context{
NSLog( @"n change = %@ n keyPath =%@ object =%@ context=%@",change ,keyPath,object,context);
/** ============================ 控制台输出 ===================================
*
change = {
kind = 1;
new = 20; --------->新的值是 20 注册监听后,observeValueForKeyPath会回调输出
old = 15; --------->旧的值是 15 注册监听前,这个属性初始化时候就是15。
}
keyPath =age object =<Person: 0x7feec962a9d0> context=(null)
change = {
kind = 1;
new = 20; --------->新的值是 20 再次传入20,observeValueForKeyPath这个还是会回调输出
old = 20; --------->旧的值是 20
}
keyPath =age object =<Person: 0x7feec962a9d0> context=(null)
*/
/**
* 由控制台输出结果得出结论,只要是对属性进行改变,不管属性的值是否变化,不区分新旧值的变化,observeValueForKeyPath都是回调,通知观察者的。
*/
}
-( void)dealloc{
/**
* 移除观察
*/
[ self.person removeObserver: self forKeyPath: @"age"];
  • 手动通知
    场景:模拟人年龄的变化,看接受通知的次数
    需要在Person类里面重写方法,具体看实现代码
      
      
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
      
      
- ( void)viewDidLoad {
[ super viewDidLoad];
self.person = [[Person alloc]init];
self.person.age = 15;
/**
* 注册监听
*/
[ self.person addObserver: self forKeyPath: @"age" options: NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context: nil];
/**
* 第二次赋值 20
*/
self.person.age = 20;
/**
* 再次赋值 20
*/
self.person.age = 20;
/**
* 详细见控制台输出,只比较新旧值
*/
}
/**
* 监听回调
*/
-( void)observeValueForKeyPath:( NSString *)keyPath ofObject:( id)object change:( NSDictionary< NSString *, id> *)change context:( void *)context{
NSLog( @"n change = %@ n keyPath =%@ object =%@ context=%@",change ,keyPath,object,context);
/** ============================ 控制台输出 ===================================
*
change = {
kind = 1;
new = 20; --------->新的值是 20 注册监听后,observeValueForKeyPath会回调输出
old = 15; --------->旧的值是 15 注册监听前,这个属性初始化时候就是15。
}
keyPath =age object =<Person: 0x7feec962a9d0> context=(null)
*/
/**
* 和自动通知输出作对比,这样明白理解。主要是在Person类里面重写了 automaticallyNotifiesObserversForKey 这个方法。以及重写age的setter方法
* 由控制台输出结果得出结论,只要监听对象的属性值前后发生改变,observeValueForKeyPath就回调,通知观察者的。
*/
}
-( void)dealloc{
/**
* 移除观察
*/
[ self.person removeObserver: self forKeyPath: @"age"];
  • Person类的实现
      
      
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
      
      
//
// Person.m
// KVO
//
// Created by XXXXXX on 15/11/9.
// Copyright © 2015年 . All rights reserved.
//
@implementation
-( void)setAge:( float)age{
/**
* 判断两值是否一样,一样就不复赋值了
*/
if (_age!= age) {
[ self willChangeValueForKey: @"age"];
_age = age;
[ self didChangeValueForKey: @"age"];
}
}
/**
* 是否对这个key开启自动发送通知
*
* @param theKey 监听的属性key
*
* @Return boolean value
*/
+ ( BOOL ) automaticallyNotifiesObserversForKey :( NSString *) theKey {
BOOL automatic = NO;
if ([theKey isEqualToString: @"age"])
{
automatic = NO;
}
else
{
= automatic [ super automaticallyNotifiesObserversForKey: theKey];
}
return automatic;
}
@end
  • Automatic and manual notification to inform observed, have their own strengths.

Note: The manual notification method must be rewritten, not only the old and new values will be the same before and after the notification. Automatic notice regardless of whether the old and new values, like, say observers tell.

Original: Big Box  KVC / KVO


Guess you like

Origin www.cnblogs.com/wangziqiang123/p/11618392.html