iOS开发--KVC&KVO

     KVC和KVO这两个词看起来很唬人的样子,很多人一开始接触的时候都不知道这是什么鬼,而且掌握了后又容易忘。所以在这里简单做个笔记,以备不时之需。下面分别介绍一下KVC和KVO的使用方法。

1.KVC

    KVC是Key-Value Coding的缩写,是键值编码的意思,它是一种可以直接通过字符串的名字(key)来访问类属性(实例变量)的机制。主要、、常用的是setValue:forKey:以及setValue:forKeyPath:这两个方法。

   (1)先来看下setValue:forKey:的用法。建一个测试类Person,头文件里有个年龄属性age。

1 #import <Foundation/Foundation.h>
2 
3 @interface Person : NSObject
4 
5 @property (nonatomic, assign) NSInteger age;
6 
7 @end

   示例1: 在基类里我们可以直接通过setter方法或者setValue:forKey:方法对Person里的age成员赋值。 

1 //setter方法赋值
2 self.person.age = 18;
3 NSLog(@"我-%ld,是通过setter方法赋予年龄的",self.person.age);
4 
5 //setValue:forKey:方法赋值,第一个参数是要设置的值,第二个参数是变量名
6 [self.person setValue:@(24) forKey:@"age"];
7 NSLog(@"我-%ld,是通过setValueForKey方法赋予年龄的",self.person.age);

   运行结果如下:

1 2016-03-14 10:07:15.135 KVC[815:66605] 我-18,是通过setter方法赋予年龄的
2 2016-03-14 10:07:15.135 KVC[815:66605] 我-24,是通过setValueForKey方法赋予年龄的

   示例2:我们也可通过getter方法或者valueForKey:方法来获取变量的值.  

1  self.person.age = 30;
2  NSLog(@"我-%ld,是通过setter方法获取年龄的",self.person.age);
3  NSLog(@"我-%@,是通过setter方法获取年龄的",[self.person valueForKey:@"age"]);

   运行结果如下: 

1 2016-03-14 10:18:20.098 KVC[869:133820] 我-30,是通过setter方法获取年龄的
2 2016-03-14 10:18:20.099 KVC[869:133820] 我-30,是通过valueForKey:方法获取年龄的

   示例3:对于Person里的私有成员属性,我们只能通过setValue:forKey:和valueForKey:方法来进行赋值和获取。

   譬如在Person.m文件里声明一个name属性。 

 1 @implementation Person
 2 {
 3     NSString *_name;
 4 }
 5 
 6 -(instancetype)init{
 7 
 8     self = [super init];
 9     if(self){
10     
11         _name = @"小明";
12     }
13     
14     return self;
15 }

   然后我们对其进行赋值或获值。  

1 //先通过valueForKey:方法获取name的值
2 NSLog(@"我原名叫-%@",[self.person valueForKey:@"name"]);
3 //再通过setValue:forKey:方法对其赋值
4 [self.person setValue:@"小王" forKey:@"name"];
5 NSLog(@"我现在叫-%@,是通过setValue:forKey:方法重命名的",[self.person valueForKey:@"name"]);

   运行结果如下: 

1 2016-03-14 10:36:55.067 KVC[941:197346] 我原名叫-小明
2 2016-03-14 10:36:55.067 KVC[941:197346] 我现在叫-小王,是通过setValue:forKey:方法重命名的

   (2)再来看下setValue:forKeyPath:的用法。这是通过键路径来给变量赋值,可以简单理解为给类的成员的成员赋值。

        譬如说,我们在上面的基础上再建一个类Address,里面有一个address的成员属性,然后在Preson里面声明。 

 1 #import <Foundation/Foundation.h>
 2 
 3 @interface Address : NSObject
 4 
 5 @property (nonatomic, strong) NSString *address;
 6 
 7 @end
 8 
 9 
10 #import <Foundation/Foundation.h>
11 @class Address;
12 
13 @interface Person : NSObject
14 
15 @property (nonatomic, assign) NSInteger age;
16 @property (nonatomic, strong) Address *addr;
17 
18 @end

     示例1.给Person的Address成员里的address属性进行赋值。

1 //通过setter方法赋值
2 self.person.addr.address = @"老家";
3  NSLog(@"我之前在-%@,是通过setter方法得到地址的",self.person.add.address);
4  //通过setValue:forKeyPath:进行赋值
5 [self.person setValue:@"深圳" forKeyPath:@"addr.address"];
6  NSLog(@"我现在在-%@,是通过setValue:forKeyPath:方法得到地址的",self.person.addr.address);

     运行结果如下:

1 2016-03-14 10:56:59.127 KVC[1046:317724] 我之前在-老家,是通过setter方法得到地址的
2 2016-03-14 10:56:59.128 KVC[1046:317724] 我现在在-深圳,是通过setValue:forKeyPath:方法得到地址的

     示例2:通过getter或者valueForKeyPath:获取address的值。  

1 self.person.addr.address = @"北京";
2 NSLog(@"我将要去-%@,通过getter方法得知目的地的",self.person.addr.address);
3 NSLog(@"我将要去-%@,通过valueForKeyPath:方法得知目的地的",[self.person valueForKeyPath:@"addr.address"]);

     运行结果如下: 

1 2016-03-14 11:04:27.547 KVC[1102:348596] 我将要去-北京,通过getter方法得知目的地的
2 2016-03-14 11:04:27.548 KVC[1102:348596] 我将要去-北京,通过valueForKeyPath:方法得知目的地的

     对于私有成员属性,情况和上面类似,就不做过多赘述。

     注:当通过key来访问类成员属性的时候,会查找这个类里与key相匹配的实例变量(_key, key, _isKey, isKey)。比如说下面这句代码:  

 

1 [self.person setValue:@"小王" forKey:@"name"];

 

会匹配_name、name、_isName、isName这四个实例变量,当同时存在时,优先级为:_name>_isName>name>isName;当这四个实例变量都不存在时,通过valueForKey:或者valueForKeyPath: 这两个方法获取变量程序会报错。      

     这里对于KVC的简单介绍就完结了,如果需要了解setValue:forKey方法的实现原理,可以参考这篇文章:http://www.jianshu.com/p/d54af904967b

 

2.KVO

     KVO是Key-Value Observing的缩写,翻译为键值观察,是一种监听机制,当指定的对象的属性被修改后,则观察者就会接受到通知。

     KVO的使用步骤:给对象添加指定路径的观察者,设置观察者的监听回调,移除观察者

     (1)在上面的基础上,设置Person为观察者,观察其成员addr的address属性的变化。 

 1 -(instancetype)init{
 2 
 3     self = [super init];
 4     if(self){
 5     
 6          _addr = [[Address alloc] init];
 7         //给_addr添加观察者为当前类self,观察其属性address的变化
 8         //若address属性发生变化,则通过回调方法observeValueForKeyPath:方法告知
 9         //context是可以传给回调方法的内容
10         [_addr addObserver:self forKeyPath:@"address" options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld context:nil];
11     }
12     
13     return self;
14 }

     (2)重写回调方法,输出addr的address属性改变前后的值。 

1 -(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context{
2 
3     NSLog(@"------ 分隔线 ------");
4     NSLog(@"原来的地址是:%@",change[NSKeyValueChangeOldKey]);
5     NSLog(@"现在的地址是:%@",change[NSKeyValueChangeNewKey]);
6 }

     (3)移除观察者。

1 -(void)dealloc{
2 
3     [_addr removeObserver:self forKeyPath:@"address"];
4 }

      然后我们改变两次addr的address属性。

1 self.person.addr.address = @"老家";
2 [self.person setValue:@"深圳" forKeyPath:@"addr.address"];

      运行结果如下:  

1 2016-03-14 11:22:44.370 KVC[1199:448041] ------ 分隔线 ------
2 2016-03-14 11:22:44.371 KVC[1199:448041] 原来的地址是:<null>
3 2016-03-14 11:22:44.371 KVC[1199:448041] 现在的地址是:老家
4 2016-03-14 11:22:44.371 KVC[1199:448041] ------ 分隔线 ------
5 2016-03-14 11:22:44.371 KVC[1199:448041] 原来的地址是:老家
6 2016-03-14 11:22:44.371 KVC[1199:448041] 现在的地址是:深圳

      这样KVO的简单流程就完成了。

posted @ 2016-03-10 17:14  黯Love幻  阅读(276)  评论(1编辑  收藏  举报