文档章节

KVC 与 KVO 理解

胡子先森
 胡子先森
发布于 2017/04/18 23:47
字数 1470
阅读 4
收藏 0

KVC 与 KVO 是 Objective C 的关键概念,个人认为必须理解的东西,下面是实例讲解。

Key-Value Coding (KVC)

kvc就是键值编码(key-value),说白了就是通过指定的key获得想要的值value。而不是通过调用Setter、Getter方法访问。

KVC,即是指 NSKeyValueCoding,一个非正式的 Protocol,提供一种机制来间接访问对象的属性。KVO 就是基于 KVC 实现的关键技术之一。

一个对象拥有某些属性。比如说,一个 Person 对象有一个 name 和一个 address 属性。以 KVC 说法,Person 对象分别有一个 value 对应他的 name 和 address 的 key。 key 只是一个字符串,它对应的值可以是任意类型的对象。从最基础的层次上看,KVC 有两个方法:一个是设置 key 的值,另一个是获取 key 的值。如下面的例子:

?

1

2

3

4

5

6

7

8

9

10

11

12

void changeName(Person *p, NSString *newName)

{

 

    // using the KVC accessor (getter) method

    NSString *originalName = [p valueForKey:@"name"];

 

    // using the KVC  accessor (setter) method.

    [p setValue:newName forKey:@"name"];

 

    NSLog(@"Changed %@'s name to: %@", originalName, newName);

 

}

现在,如果 Person 有另外一个 key 配偶(spouse),spouse 的 key 值是另一个 Person 对象,用 KVC 可以这样写:

 

?

1

2

3

4

5

6

7

8

9

10

11

12

13

void logMarriage(Person *p)

{

 

    // just using the accessor again, same as example above

    NSString *personsName = [p valueForKey:@"name"];

 

    // this line is different, because it is using

    // a "key path" instead of a normal "key"

    NSString *spousesName = [p valueForKeyPath:@"spouse.name"];

 

    NSLog(@"%@ is happily married to %@", personsName, spousesName);

 

}

key 与 key path 要区分开来,key 可以从一个对象中获取值,而 key path 可以将多个 key 用点号 “.” 分割连接起来,比如:

[p valueForKeyPath:@"spouse.name"];

相当于这样……

[[p valueForKey:@"spouse"] valueForKey:@"name"];

 

KVC的强大之处:

1 .访问私有变量

一个类中的私有变量,不能直接通过Setter、Getter方法访问。但是却可以通过KVC来访问。举例:

一个类如下:

@interface Dog : NSObject
{
    @private
    double height;
}

这里的私有变量 height ,假如直接使用Setter、Getter方法访问,就会出现下面的错误。

这里写图片描述

这时候可以使用KVC来访问这个私有变量:

Dog *dog = [[Dog alloc]init];
[dog setValue:@12 forKey:@"height"];//给私有变量赋值
NSLog(@"dog's height is = %@",[dog valueForKey:@"height"]);//读取私有变量的值
  • 实际使用举例
    利用kvc的这个特性,我们可以访问系统里的一些私有变量。
    例如:在UIPageControl里面有两个私有变量:
UIImage*        _currentPageImage;
UIImage*        _pageImage;

我们可以通过kvc来进行读取和赋值:

UIPageControl *pageControl = [[UIPageControl alloc]init];
//设置值
[pageControl setValue:[UIImage imageNamed:@"XX"] forKeyPath:@"_currentPageImage"];

[pageControl setValue:[UIImage imageNamed:@"XX"] forKeyPath:@"_pageImage"];
//读取值
UIImage *currentImage = [pageControl valueForKey:@"_currentPageImage"];

UIImage *pageImage = [pageControl valueForKey:@"_pageImage"];

这样我们就可以设置当前显示和未显示到脚标的样式了。

2. 使用KVC直接访问 NSArray 或者 NSSet 的属性值

NSArray/NSSet等都支持KVC,这里举一个例子:

NSArray *books= @[book1, book2, book3];
NSArray *names = [books valueForKeyPath:@"name"];
NSLog(@"%@", [books valueForKeyPath:@"@avg.price"]);//使用kvc直接打印出来书的平均价格

其中,book是一个书类,有一个属性是name,一个属性是price。

3. 使用KVC将字典(json)转化成模型

 // 定义一个字典
    NSDictionary *dict = @{
                           @"name"  : @"jack",
                           @"money" : @"20.7",
                        };
    // 创建模型
    Person *p = [[Person alloc] init];

    // 字典转模型
    [p setValuesForKeysWithDictionary:dict];
    NSLog(@"person's name is the %@",p.name);
}

注意:

(1). key的值必须正确,如果拼写错误,会出现异常

(2). 当key的值是没有定义的,valueForUndefinedKey:这个方法会被调用,如果你自己写了这个方法,key的值出错就会调用到这里来

(3). 因为类key反复嵌套,所以有个keyPath的概念,keyPath就是用.号来把一个一个key链接起来,这样就可以根据这个路径访问下去

(4). NSArray/NSSet等都支持KVC

好了,以上是 KVC 的基本知识,接着看看 KVO。

Key-Value Observing (KVO)

Key-Value Observing (KVO) 建立在 KVC 之上,它能够观察一个对象的 KVC key path 值的变化。

一般分为三步:

  1. 注册监听

使用方法:

/**
*  添加KVO监听者
*
*  @param observer 观察者(监听器)
*  @param keyPath  属性名(要观察的属性)
*  @param options
*  @param context  传递的参数
*/
- (void)addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(void *)context;

例子:

//为对象p添加一个观察者(监听器)
[p addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew context:@"test"];

2.设置监听事件

/**
 *  当利用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);
}

3.取消监听

//释放KVO监听
-(void)dealloc
{
    [p removeObserver:self forKeyPath:@"test"];
}

 

举个例子,用代码观察一个 person 对象的 address 变化,以下是实现的三个方法:

  • watchPersonForChangeOfAddress: 实现观察
  • observeValueForKeyPath:ofObject:change:context: 在被观察的 key path 的值变化时调用。
  • dealloc 停止观察

 

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

static NSString *const KVO_CONTEXT_ADDRESS_CHANGED = @"KVO_CONTEXT_ADDRESS_CHANGED"

 

@implementation PersonWatcher

 

-(void) watchPersonForChangeOfAddress:(Person *)p

{

 

    // this begins the observing

    [p addObserver:self

        forKeyPath:@"address"

           options:0

           context:KVO_CONTEXT_ADDRESS_CHANGED];

 

    // keep a record of all the people being observed,

    // because we need to stop observing them in dealloc

    [m_observedPeople addObject:p];

}

 

// whenever an observed key path changes, this method will be called

- (void)observeValueForKeyPath:(NSString *)keyPath

                      ofObject:(id)object

                        change:(NSDictionary *)change

                       context:(void *)context

 

{

    // use the context to make sure this is a change in the address,

    // because we may also be observing other things

    if(context == KVO_CONTEXT_ADDRESS_CHANGED) {

        NSString *name = [object valueForKey:@"name"];

        NSString *address = [object valueForKey:@"address"];

        NSLog(@"%@ has a new address: %@", name, address);

    }

}

 

-(void) dealloc;

{

 

    // must stop observing everything before this object is

    // deallocated, otherwise it will cause crashes

    for(Person *p in m_observedPeople){

        [p removeObserver:self forKeyPath:@"address"];

    }

 

    [m_observedPeople release];

    m_observedPeople = nil;

 

    [super dealloc];

 

}

 

-(id) init;

{

    if(self = [super init]){

        m_observedPeople = [NSMutableArray new];

    }

 

    return self;

}

 

@end

这就是 KVO 的作用,它通过 key path 观察对象的值,当值发生变化的时候会收到通知。

本文转载自:https://magicalboy.com/kvc_and_kvo/

上一篇: iOS中的KeyChain
下一篇: iOS内存管理
胡子先森
粉丝 0
博文 19
码字总数 3019
作品 0
通州
私信 提问
KVC 与 KVO 理解

KVC 与 KVO 是 Objective C 的关键概念,个人认为必须理解的东西,下面是实例讲解。 Key-Value Coding (KVC) KVC,即是指 NSKeyValueCoding,一个非正式的 Protocol,提供一种机制来间接访问...

Im刘亚芳
2014/12/04
0
0
KVO和KVC的关系

KVC 与 KVO 是 Objective C 的关键概念,个人认为必须理解的东西,下面是实例讲解。 Key-Value Coding (KVC) KVC,即是指 NSKeyValueCoding,一个非正式的 Protocol,提供一种机制来间接访问...

刘三火
2016/04/21
71
0
KVC/KVO 的使用及原理分析

KVC/KVO 概念 KVC : 即 Key-Value-Coding,用于键值编码。作为 cocoa 的一个标准化组成部分,它是基于 NSKeyValueCoding 非正式协议的机制。简单来说,就是直接通过 key 值对对象的属性进行...

满脸胡茬的小码农
2017/11/14
0
0
iOS基础黑科技:KVO(键值观察)以及KVC(键值编码)

一、前言: 许多大神的博客都有关于KVO以及KVC的分析,在这里我整理一下自己关于KVO以及KVC的理解。求大神们轻喷~~ 二、基本概念: 1、键值编码(KVC):顾名思义,键值编码是一种访问对象属...

MichaelLee_
2017/12/19
0
0
程序员面试闪充 -- KVC&KVO

一、键值编码KVC kvc&kvo视频讲解 1、介绍 由于oc的语言特性,使得开发者根本不必进行任何操作就可以进行属性的动态读写,这种方式就是Key Value Coding(简称KVC)。 KVC的操作方法由NSKey...

阡陌有客
2018/03/20
0
0

没有更多内容

加载失败,请刷新页面

加载更多

OpenStack 简介和几种安装方式总结

OpenStack :是一个由NASA和Rackspace合作研发并发起的,以Apache许可证授权的自由软件和开放源代码项目。项目目标是提供实施简单、可大规模扩展、丰富、标准统一的云计算管理平台。OpenSta...

小海bug
昨天
5
0
DDD(五)

1、引言 之前学习了解了DDD中实体这一概念,那么接下来需要了解的就是值对象、唯一标识。值对象,值就是数字1、2、3,字符串“1”,“2”,“3”,值时对象的特征,对象是一个事物的具体描述...

MrYuZixian
昨天
6
0
数据库中间件MyCat

什么是MyCat? 查看官网的介绍是这样说的 一个彻底开源的,面向企业应用开发的大数据库集群 支持事务、ACID、可以替代MySQL的加强版数据库 一个可以视为MySQL集群的企业级数据库,用来替代昂贵...

沉浮_
昨天
6
0
解决Mac下VSCode打开zsh乱码

1.乱码问题 iTerm2终端使用Zsh,并且配置Zsh主题,该主题主题需要安装字体来支持箭头效果,在iTerm2中设置这个字体,但是VSCode里这个箭头还是显示乱码。 iTerm2展示如下: VSCode展示如下: 2...

HelloDeveloper
昨天
7
0
常用物流快递单号查询接口种类及对接方法

目前快递查询接口有两种方式可以对接,一是和顺丰、圆通、中通、天天、韵达、德邦这些快递公司一一对接接口,二是和快递鸟这样第三方集成接口一次性对接多家常用快递。第一种耗费时间长,但是...

程序的小猿
昨天
10
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部