OC学习——KVC KVO NSNotification

一、KVC
        KVC(KeyValueCoding) “键-值-编码”是一种可以直接通过字符串的名字(key)来访问类实例变量的机制,是通过setter、getter方法访问。属性的访问和设置
        KVC可以用来访问和设置实例变量的值。key是属性名称
    设置方式:[self setValue:aName forKey:@"name"]
      等同于:self.name = aName;
    访问方式:aString = [self valueForKey:@"name"]

      等同于:aString = self.name;

核心内容:

    [对象 setValue aValue forKey aKey];  //对象的变量赋值

    aValue = [对象 valueForKey aKey];  //把变量值取出来   

for example:

         

       

1、设置属性值

  1. //设置值  
  2. //这里setValue方法:第一个参数是value,第二个参数是key(就是类的属性名称)  
  3. [p setValue:@"jiangwei" forKey:@"name"];  
  4.           
  5. Dog *dog = [[Dog alloc] init];  
  6. [p setValue:dog forKey:@"dog"];  
使用setValue方法,就可以进行对属性进行设置值操作了,同时需要传递这个属性的名称,这个和Java中使用反射机制真的很像。

注:KVC设置值时,如果属性有set方法,则优先调用set方法,如果没有则直接设置上去,get方法一样

  1. //设置基本数据类型  
  2. //这里需要将基本类型转化成NSNumber  
  3. //在设置值的时候,会有自动解包的过程,NSNumber会解包赋值给age  
  4. [p setValue:@22 forKey:@"age"];  
还有一个需要注意的地方:当我们在设置基本类型的时候,需要将其转化成NSNumber类型的。

2、取属性的值


  1. //读取值  
  2. NSString *name = [p valueForKey:@"name"];  
取值就简单了


下面再来看一下KVC中强大的功能:键值路径

键值路径是对于一个类中有数组对象的属性进行便捷操作。

看个场景:

一个作者有多本书

Author.h

  1. //  
  2. //  Author.h  
  3. //  43_KeyValuePath  
  4. //  
  5.   
  6. #import <Foundation/Foundation.h>  
  7.   
  8. @interface Author : NSObject{  
  9.     NSString *_name;  
  10.       
  11.     //作者出版的书,一个作者对应多个书籍对象  
  12.     NSArray *_issueBook;  
  13. }  
  14.   
  15. @end  

作者类中定义了名字和一个书籍数组


Author.m
  1. //  
  2. //  Author.m  
  3. //  43_KeyValuePath  
  4. //  
  5.   
  6. #import "Author.h"  
  7.   
  8. @implementation Author  
  9.   
  10. @end  


Book.h

  1. //  
  2. //  Book.h  
  3. //  43_KeyValuePath  
  4. //  
  5.   
  6. #import <Foundation/Foundation.h>  
  7. #import "Author.h"  
  8.   
  9. @interface Book : NSObject{  
  10.     Author *_author;  
  11. }  
  12.   
  13. @property NSString *name;  
  14. @property floatfloat *price;  
  15.   
  16. @end  
定义了一个作者属性,书的名字,价格


Book.m

  1. //  
  2. //  Book.m  
  3. //  43_KeyValuePath  
  4. //  
  5.   
  6. #import "Book.h"  
  7.   
  8. @implementation Book  
  9.   
  10. @end  

看一下测试代码

main.m

[objc]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. //  
  2. //  main.m  
  3. //  43_KeyValuePath   
  4. //  
  5.   
  6. #import <Foundation/Foundation.h>  
  7. #import "Book.h"  
  8. #import "Author.h"  
  9.   
  10. int main(int argc, const charchar * argv[]) {  
  11.     @autoreleasepool {  
  12.           
  13.         //------------------KVC键值路径  
  14.         /* 
  15.         Book *book = [[Book alloc] init]; 
  16.         Author *author = [[Author alloc] init]; 
  17.          
  18.         //设置作者 
  19.         [book setValue:author forKey:@"author"]; 
  20.          
  21.         //设置作者的名字 
  22.         //路径为:author.name,中间用点号进行连接 
  23.         [book setValue:@"jiangwei" forKeyPath:@"author.name"]; 
  24.         NSString *name = [author valueForKey:@"name"]; 
  25.         NSLog(@"name is %@",name); 
  26.          */  
  27.           
  28.           
  29.         //--------------------KVC的运算  
  30.         Author *author = [[Author alloc] init];  
  31.         [author setValue:@"莫言" forKeyPath:@"name"];  
  32.           
  33.         Book *book1 = [[Book alloc] init];  
  34.         book1.name = @"红高粱";  
  35.         book1.price = 9;  
  36.         Book *book2 = [[Book alloc] init];  
  37.         book2.name = @"蛙";  
  38.         book2.price = 10;  
  39.         NSArray *array = [NSArray arrayWithObjects:book1,book2, nil nil];  
  40.         [author setValue:array forKeyPath:@"issueBook"];  
  41.           
  42.         //基本数据类型会自动被包装成NSNumber,装到数组中  
  43.         //得到所有书籍的价格  
  44.         NSArray *priceArray = [author valueForKeyPath:@"issueBook.price"];  
  45.         NSLog(@"%@",priceArray);  
  46.           
  47.         //获取数组的大小  
  48.         NSNumber *count = [author valueForKeyPath:@"issueBook.@count"];  
  49.         NSLog(@"count=%@",count);  
  50.           
  51.         //获取书籍价格的总和  
  52.         NSNumber *sum = [author valueForKeyPath:@"issueBook.@sum.price"];  
  53.         NSLog(@"%@",sum);  
  54.           
  55.         //获取书籍的平均值  
  56.         NSNumber *avg = [author valueForKeyPath:@"issueBook.@avg.price"];  
  57.         NSLog(@"%@",avg);  
  58.           
  59.         //获取书籍的价格最大值和最小值  
  60.         NSNumber *max = [author valueForKeyPath:@"issueBook.@max.price"];  
  61.         NSNumber *min = [author valueForKeyPath:@"issueBook.@min.price"];  
  62.           
  63.     }  
  64.     return 0;  
  65. }  
1、首先通过前面说到的KVC设置作者的书籍数组
  1. //--------------------KVC的运算  
  2. Author *author = [[Author alloc] init];  
  3. [author setValue:@"莫言" forKeyPath:@"name"];  
  4.   
  5. Book *book1 = [[Book alloc] init];  
  6. book1.name = @"红高粱";  
  7. book1.price = 9;  
  8. Book *book2 = [[Book alloc] init];  
  9. book2.name = @"蛙";  
  10. book2.price = 10;  
  11. NSArray *array = [NSArray arrayWithObjects:book1,book2, nil nil];  
  12. [author setValue:array forKeyPath:@"issueBook"];  
添加了两本书籍


2、下面就开始用到KVC中键值路径了

1)获取作者类中书籍数组中所有书籍的价格

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. //基本数据类型会自动被包装成NSNumber,装到数组中  
  2. //得到所有书籍的价格  
  3. NSArray *priceArray = [author valueForKeyPath:@"issueBook.price"];  
  4. NSLog(@"%@",priceArray);  
看到了:@"issueBook.price" 这就是键值路径的使用,issueBook是作者类中的书籍数组属性名,price是书籍类的属性,中间用点号进行连接,这样我们就可以获取到了所有书籍的价格了,如果在Java中,我们需要用一个循环操作。但是OC中多么方便。


2)获取作者类中书籍数组的大小

  1. //获取数组的大小  
  2. NSNumber *count = [author valueForKeyPath:@"issueBook.@count"];  
  3. NSLog(@"count=%@",count);  
使用 @"issueBook.@count" 键值路径获取书籍数组的大小,issueBook是作者类中的书籍数组属性名,@count是特定一个写法,可以把它想象成一个方法,中间任然用点号进行连接


3)获取作者类中书籍数组的价格总和

  1. //获取书籍价格的总和  
  2. NSNumber *sum = [author valueForKeyPath:@"issueBook.@sum.price"];  
  3. NSLog(@"%@",sum);  
使用 @"issueBook.@sum.price" 键值路径获取书籍数组中的价格总和,issueBook是作者类中的书籍数组属性名,@sum是特性写法,可以把它想象成一个方法,price是书籍的价格属性名,可以把它看成是@sum的一个参数,中间用点号进行连接

如果在java中,这个需要用一个循环来计算总和,OC中很方便的


4)获取作者类中书籍数组的价格平均值、最小值、最大值

  1. //获取书籍的平均值  
  2. NSNumber *avg = [author valueForKeyPath:@"issueBook.@avg.price"];  
  3. NSLog(@"%@",avg);  
  4.   
  5. //获取书籍的价格最大值和最小值  
  6. NSNumber *max = [author valueForKeyPath:@"issueBook.@max.price"];  
  7. NSNumber *min = [author valueForKeyPath:@"issueBook.@min.price"];  

操作和上面类似,这里就不解释了

我们看到上面返回来的数据都是NSNumber类型的


2、KVO 观察者
        KVO(KeyValueObserver) “键-值-监听”定义了这样一种机制,当对象的属性值发生变化的时候,我们能收到一个“通知”。观察者更准确
NSObject提供了监听机制。所有子类也就全都能进行监听
        KVO是基于KVC来实现的。
        实现监听步骤
      (1)注册监听对象。anObserver指监听者,keyPath就是要监听的属性值,而context方便传输你需要的数据,它是个指针类型。

                    

              其中, options是监听的选项,也就是说明监听返回的字典包含什么值。有两个常用的选项:
                     NSKeyValueObservingOptio nNew 指返回的字典包含新值。
                     NSKeyValueObservingOptio nOld       指返回的字典包含旧值。
     (2)实现监听方法。监听方法在Value(属性的值)发生变化的时候自动调用。  
                   
              其中,object指被监听的对象。change里存储了一些变化的数据,比如变化前的数据,变化后的数据。


             

KVO操作在OC中也是经常会用到的,而且这种机制在java中不存在的。

它的作用就是用来监听类中属性值的变化,实现原理是观察者模式,当然我们也可以使用观察者模式在Java中实现这样的机制

看一下具体的例子:现在有一个小孩类,他有两个属性:开心值,饥饿值,然后还有一个护士类,用来监听孩子类的这两个属性值的

Chidren.h

[objc]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. //  
  2. //  Children.h  
  3. //  44_KVO  
  4. //  
  5. //  Created by jiangwei on 14-10-16.  
  6. //  Copyright (c) 2014年 jiangwei. All rights reserved.  
  7. //  
  8.   
  9. #import <Foundation/Foundation.h>  
  10.   
  11. @interface Children : NSObject  
  12.   
  13. @property NSInteger *hapyValue;  
  14. @property NSInteger *hurryValue;  
  15.   
  16.   
  17. @end  
Children.m

[objc]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. //  
  2. //  Children.m  
  3. //  44_KVO  
  4. //  
  5. //  Created by jiangwei on 14-10-16.  
  6. //  Copyright (c) 2014年 jiangwei. All rights reserved.  
  7. //  
  8.   
  9. #import "Children.h"  
  10.   
  11. @implementation Children  
  12.   
  13. - (id) init{  
  14.     self = [super init];  
  15.     if(self != nil){  
  16.         //启动定时器  
  17.         [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(timerAction) userInfo:nil repeats:YES];  
  18.         self.hapyValue100;  
  19.     }  
  20.     return self;  
  21. }  
  22.   
  23. - (void) timerAction:(NSTimer *) timer{  
  24.     //使用set方法修改属性值,才能触发KVO  
  25.       
  26.     int value = _hapyValue;  
  27.     [self setHapyValue:--value];  
  28.       
  29.     int values = _hurryValue;  
  30.     [self setHurryValue:--values];  
  31. }  
  32.   
  33.   
  34. @end  
在初始化方法中,我们启动一个定时器,然后隔1s就去修改孩子类的值


Nure.h

[objc]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. //  
  2. //  Nure.h  
  3. //  44_KVO  
  4. //  
  5. //  Created by jiangwei on 14-10-16.  
  6. //  Copyright (c) 2014年 jiangwei. All rights reserved.  
  7. //  
  8.   
  9. #import <Foundation/Foundation.h>  
  10.   
  11. @class Children;  
  12. @interface Nure : NSObject{  
  13.     Children *_children;  
  14. }  
  15.   
  16. - (id) initWithChildren:(Children *)children;  
  17.   
  18. @end  
定义一个孩子属性

Nure.m

[objc]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. //  
  2. //  Nure.m  
  3. //  44_KVO  
  4. //  
  5. //  Created by jiangwei on 14-10-16.  
  6. //  Copyright (c) 2014年 jiangwei. All rights reserved.  
  7. //  
  8.   
  9. #import "Nure.h"  
  10. #import "Children.h"  
  11.   
  12. @implementation Nure  
  13.   
  14. - (id) initWithChildren:(Children *)children{  
  15.     self = [super init];  
  16.     if(self != nil){  
  17.         _children = children;  
  18.           
  19.         //观察小孩的hapyValue  
  20.         //使用KVO为_children对象添加一个观察者,用于观察监听hapyValue属性值是否被修改  
  21.         [_children addObserver:self forKeyPath:@"hapyValue" options:NSKeyValueObservingOptionNew |NSKeyValueObservingOptionOld context:@"context"];  
  22.           
  23.         //观察小孩的hurryValue  
  24.         [_children addObserver:self forKeyPath:@"hurryValue" options:NSKeyValueObservingOptionNew |NSKeyValueObservingOptionOld context:@"context"];  
  25.     }  
  26.     return self;  
  27. }  
  28.   
  29. //触发方法  
  30. - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(voidvoid *)context{  
  31.     NSLog(@"%@",change);  
  32.     //通过打印change,我们可以看到对应的key  
  33.       
  34.     //通过keyPath来判断不同属性的观察者  
  35.     if([keyPath isEqualToString:@"hapyValue"]){  
  36.         //这里change中有old和new的值是因为我们在调用addObserver方法时,用到了  
  37.         //NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld;想要哪一个就用哪一个  
  38.         //[change objectForKey:@"old"]是修改前的值  
  39.         NSNumber *hapyValue = [change objectForKey:@"new"];//修改之后的最新值  
  40.           
  41.         NSInteger *value = [hapyValue integerValue];  
  42.           
  43.         if(value < 90){  
  44.             //do something...  
  45.         }  
  46.     }else if([keyPath isEqualToString:@"hurryValue"]){  
  47.         //这里change中有old和new的值是因为我们在调用addObserver方法时,用到了  
  48.         //NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld;想要哪一个就用哪一个  
  49.         //[change objectForKey:@"old"]是修改前的值  
  50.         NSNumber *hurryValue = [change objectForKey:@"new"];//修改之后的最新值  
  51.           
  52.         NSInteger *value = [hurryValue integerValue];  
  53.           
  54.         if(value < 90){  
  55.             //do something...  
  56.         }  
  57.     }  
  58.       
  59.     NSLog(@"%@",context);//打印的就是addObserver方法的context参数  
  60.       
  61.       
  62.       
  63.     //使用KVC去修改属性的值,也会触发事件  
  64. }  
  65.   
  66. - (void)dealloc{  
  67.       
  68.     //移除观察者  
  69.     [_children removeObserver:self forKeyPath:@"hapyValue"];  
  70.     [_children removeObserver:self forKeyPath:@"hurryValue"];  
  71.       
  72. }  
  73.   
  74. @end  
看到了在这里就开始进行监听操作了


下面来具体看一下如何做到监听的

1、添加监听对象

我们使用addObserver方法给孩子添加监听对象

第一个参数:监听者,这里是Nure,所以可以直接传递self

第二个参数:监听对象的属性名

第三个参数:监听这个属性的状态:这里可以使用|进行多种组合操作,属性的新值和旧值

第四个参数:传递内容给监听方法

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. //观察小孩的hapyValue  
  2. //使用KVO为_children对象添加一个观察者,用于观察监听hapyValue属性值是否被修改  
  3. [_children addObserver:self forKeyPath:@"hapyValue" options:NSKeyValueObservingOptionNew |NSKeyValueObservingOptionOld context:@"context"];  
  4.   
  5. //观察小孩的hurryValue  
  6. [_children addObserver:self forKeyPath:@"hurryValue" options:NSKeyValueObservingOptionNew |NSKeyValueObservingOptionOld context:@"context"];  

2、监听方法

[objc]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. //触发方法  
  2. - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(voidvoid *)context{  
  3.     NSLog(@"%@",change);  
  4.     //通过打印change,我们可以看到对应的key  
  5.       
  6.     //通过keyPath来判断不同属性的观察者  
  7.     if([keyPath isEqualToString:@"hapyValue"]){  
  8.         //这里change中有old和new的值是因为我们在调用addObserver方法时,用到了  
  9.         //NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld;想要哪一个就用哪一个  
  10.         //[change objectForKey:@"old"]是修改前的值  
  11.         NSNumber *hapyValue = [change objectForKey:@"new"];//修改之后的最新值  
  12.           
  13.         NSInteger *value = [hapyValue integerValue];  
  14.           
  15.         if(value < 90){  
  16.             //do something...  
  17.         }  
  18.     }else if([keyPath isEqualToString:@"hurryValue"]){  
  19.         //这里change中有old和new的值是因为我们在调用addObserver方法时,用到了  
  20.         //NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld;想要哪一个就用哪一个  
  21.         //[change objectForKey:@"old"]是修改前的值  
  22.         NSNumber *hurryValue = [change objectForKey:@"new"];//修改之后的最新值  
  23.           
  24.         NSInteger *value = [hurryValue integerValue];  
  25.           
  26.         if(value < 90){  
  27.             //do something...  
  28.         }  
  29.     }  
  30.       
  31.     NSLog(@"%@",context);//打印的就是addObserver方法的context参数  
  32.       
  33.       
  34.       
  35.     //使用KVC去修改属性的值,也会触发事件  
  36. }  
我们上面传递的第一个参数是监听者,这个方法也是在监听者中实现的,当属性值发生变化的时候,这个方法会被回调

这个方法的参数:

第一个参数:键值路径

第二个参数:监听对象

第三个参数:变化的值

第四个参数:传递的内容

我们看到代码中有一个特殊的参数:第三个参数:NSDirctionary类型的

其实我们如果不知道是干什么的,我们可以打印一下他的结果看一下,很简单,这里就不截图说明了

我们会发现他有两个键值对

key是:new和old

他们就是分别代表这个属性值变化的前后值,同时他们的得到也和之前我们添加监听对象时设置的第三个参数有关:

NSKeyValueObservingOptionNew |NSKeyValueObservingOptionOld

那个地方设置了几种状态,这里的NSDirctionary中就会有几个键值对


3、销毁方法

这个并不属于KVO的内容了,只是在这里用到了就顺便说一下

[objc]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. - (void)dealloc{  
  2.       
  3.     //移除观察者  
  4.     [_children removeObserver:self forKeyPath:@"hapyValue"];  
  5.     [_children removeObserver:self forKeyPath:@"hurryValue"];  
  6.       
  7. }  
我们在创建一个对象的时候会调用alloc方法,当对象被销毁的时候会调用dealloc这个方法,这个和C++中的析构函数一样,Java中有垃圾回收器,所以没有此类的方法,但是有一个finalize方法,其实这个方法就是在垃圾回收器回收对象的时候会调用,和这个功能差不多,但是在Java中,我们并不提倡使用这个方法。因为会造成GC的回收发生错误。

我们在销毁方法中需要移除监听者

总结

这一篇就介绍了OC中比较有特色的两个机制:KVC和KVO

KVC:就是可以暴力的去get/set类的私有属性,同时还有强大的键值路径对数组类型的属性进行操作

KVO:监听类中属性值变化的



三、通知

通知是iOS开发框架中的一种设计模式,内部的实现机制由Cocoa框架支持。通知一般用于M、V、C的间的信息传递。像我在设置页面设置App皮肤。
M是modol模型 V是view视图 C是control控制器。
系统通知
        //注册通知

           

           其中,selector是方法名     class是描述类的类    SEL method=@selector(方法名)

通知用完要移除

             



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值