iOS开发之KVC、KVO

来源:互联网 发布:淘宝里的天猫店 编辑:程序博客网 时间:2024/05/17 08:28

在此声明:此内容是之前在网上查找的例子自己下去练习写的Demo,原文地址已忘记,若有雷同,还请原作谅解。若内容有不足之处,还请大家多多指教。

KVO

 一,概述
     KVO,即:Key-Value Observing,它提供一种机制,当指定的对象的属性被修改后,则对象就会接受到通知。简单的说就是每次指定的被观察的对象的属性被修改后,KVO就会自动通知相应的观察者了。
     
 二,使用方法
     系统框架已经支持KVO,所以程序员在使用的时候非常简单。
     1. 注册,指定被观察者的属性,
     2. 实现回调方法
     3. 移除观察

三,实例:
     假设一个场景,股票的价格显示在当前屏幕上,当股票价格更改的时候,实时显示更新其价格。
     
四, 模式
     KVO也分2种,一种是自动的KVO,一种是手动的KVO。
     NSObject自动支持KVO特性并且默认情况下一个类的属性支持kvc都可以使用。如果你遵循标准的Cocoa编码和命名规则,你可以使用自动更改通知你不需要写任何额外的代码。手动更改通知提供额外的控制时发出的通知,并要求额外的编码。你可以通过类的方法automaticallynotifiesobserversforkey:控制您的类属性自动通知。
     
     自动模式的KVO:NSObject提供了基本的自动键-值改变通知。
     
     手动模式的KVO:实现手动通知的对象必须实现 automaticallyNotifiesObserversForKey 方法。为了实现手动观察消息,你必须在改变值之前调用 willChangeValueForKey: ,改变值之后调用 didChangeValueForKey:

附上代码:

StockData.h

#import <Foundation/Foundation.h>
// stock 股票
@interface StockData : NSObject
{
    NSString *stockName;//股票名
    float price;//股票价格
}
@end



Walker.h

#import <Foundation/Foundation.h>
@interface Walker : NSObject
{
    NSString *_name;
    int _age;
}
@property(nonatomic,copy)NSString *name;
@property(nonatomic,assign)int age;
-(instancetype)initWithName:(NSString *)name age:(int)age;
@end


Walker.m

#import "Walker.h"
@implementation Walker
-(instancetype)initWithName:(NSString *)name age:(int)age
{
    if (self = [super init])
    {
        _name=name;
        _age=age;
    }
    return self;
}
@end


#import "ViewController.h"
#import "StockData.h"
#import "Walker.h"
@interface ViewController ()
{
    StockData *_stockKVO;
    UILabel *_myLabel;
    
    Walker *_walker;
    UILabel *_ageLabel;
    
    UIButton * btn;
}

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    
    [self initStock];
   
    [self initWalker];
    
    btn = [UIButton buttonWithType:UIButtonTypeCustom];
    btn.frame = CGRectMake(40, 60, 200, 80);
    [btn setTitle:@"百变大咖秀" forState:UIControlStateNormal];
    btn.backgroundColor=[UIColor greenColor];
    [btn addTarget:self action:@selector(buttonAction) forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview:btn];

}
// 股票信息
- (void)initStock
{
    
    _stockKVO = [[StockData alloc] init];
    [_stockKVO setValue:@"东方航空" forKey:@"stockName"];
    [_stockKVO setValue:@"200" forKey:@"price"];
    NSLog(@"stockName===%@",[_stockKVO valueForKey:@"stockName"]);
    NSLog(@"price===%@",[_stockKVO valueForKey:@"price"]);
    
    // 1 注册监听
    [_stockKVO addObserver:self forKeyPath:@"price" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:nil];
    
    _myLabel = [[UILabel alloc]initWithFrame:CGRectMake(100, 300, 100, 40 )];
    _myLabel.backgroundColor = [UIColor grayColor];
    _myLabel.text = [NSString stringWithFormat:@"股票:%@",[_stockKVO valueForKey:@"price"]];
    [self.view addSubview:_myLabel];

}
// 年龄
- (void)initWalker
{
    _walker = [[Walker alloc] initWithName:@"JohnSon" age:22];
    [_walker addObserver:self forKeyPath:@"age" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:nil];
    
    _ageLabel = [[UILabel alloc]initWithFrame:CGRectMake(100, 400, 100, 40 )];
    _ageLabel.backgroundColor = [UIColor redColor];
    _ageLabel.text = [NSString stringWithFormat:@"年龄:%@",[_walker valueForKey:@"age"]];
    [self.view addSubview:_ageLabel];
}
//当点击button的时候,调用buttonAction方法,修改对象的属性
-(void) buttonAction
{    
    [_stockKVO setValue:@"20000.0" forKey:@"price"];
    
    _walker.age-=2;
}
// 2. 实现回调方法
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
    if ([keyPath isEqualToString:@"price"])
    {
        _myLabel.text = [NSString stringWithFormat:@"%@",[_stockKVO valueForKey:@"price"]];
        _ageLabel.text = [NSString stringWithFormat:@"%@",[_walker valueForKey:@"age"]];
    }
}


KVC

KVC是KeyValue Coding的简称,它是一种可以直接通过字符串的名字(key)来访问类属性的机制。而不是通过调用Setter、Getter方法访问。
     
         2.2.1         获取值
         valueForKey:,传入NSString属性的名字。
         valueForKeyPath:,传入NSString属性的路径,xx.xx形式。
         valueForUndefinedKey它的默认实现是抛出异常,可以重写这个函数做错误处理。
         
         2.2.2         修改值
         setValue:forKey:
         setValue:forKeyPath:
         setValue:forUndefinedKey:
         setNilValueForKey: 当对非类对象属性设置nil时,调用,默认抛出异常。
         
         2.2.3         一对多关系成员的情况
         mutableArrayValueForKey:有序一对多关系成员  NSArray
         mutableSetValueForKey:无序一对多关系成员  NSSet

Book.h

#import <Foundation/Foundation.h>
@class Author;
@interface Book : NSObject
{
    NSString *name;
    Author *author;
    float price;
    NSArray *relativeBooks;
}
@end

Author.h

#import <Foundation/Foundation.h>
@interface Author : NSObject
{
    NSString *authorName;
}
@end

- (void)viewDidLoad {
    [super viewDidLoad];

    //示例1:基本赋值取值
    Book *book = [[Book alloc] init];
    [book setValue:@"书名叫:《Objective-C入门》" forKey:@"name"];
    // 取值
    NSString *name = [book valueForKey:@"name"];
    NSLog(@"name===%@",name);
    
    //示例2:使用路径 valueForKeyPath
    Author *author = [[Author alloc] init];
    [author setValue:@"作者名叫:JohnSon" forKey:@"authorName"];
    [book setValue:author forKey:@"author"];
    
    NSString *authorName = [book valueForKeyPath:@"author.authorName"];
    NSLog(@"authorName===%@",authorName);
    
    // 示例3:自动装箱拆箱
    // KVC还有一个很重要的特点,自动装箱拆箱功能。这在ObjC中是仅有的,其他情况下均需要使用比如NSNumber来手动拆装箱的。
    [book setValue:author forKey:@"author"];
    [book setValue:@"张三" forKeyPath:@"author.authorName"];
    [book setValue:@"100" forKey:@"price"];
    NSString *bookPrice = [book valueForKey:@"price"];
    
    NSLog(@"book price is %@",bookPrice);

    /**
        输出结果为:2015-01-28 11:37:45.086 KVC[4320:68554] book price is 100
        可以看到给bookPrice输入的是NSString类型,但是没有问题,因为KVC方式会根据字符串自动转型为适当的数值。再看打印bookPrice属性,%@是打印对象,而price属性是float基本型,这里KVC肯定做了自动装箱的处理,将基本型转为NSNumber对象。
     */
    
    // 示例4:对集合的操作能力
    // KVC还具备对集合的操作能力。比如,图书可以有相关图书,这是个1对多的关系。可以用集合来表示,这里用NSArray表示
    // 如果想得到相关图书的价格NSArray,可以使用KVC方式
    
    Book *book1 = [[Book alloc] init];
    [book1 setValue:@"20" forKey:@"price"];
    
    Book *book2 = [[Book alloc] init];
    [book2 setValue:@"30" forKey:@"price"];
    
    NSArray *bookPrices = [NSArray arrayWithObjects:book1,book2, nil];
    [book setValue:bookPrices forKey:@"relativeBooks"];
    
    NSString *booksPrice = [book valueForKeyPath:@"relativeBooks.price"];
    NSLog(@"relative books price: %@",booksPrice);
    
    // 示例5:对集合做运算
    // KVC还能对集合做运算,比如想得到相关图书的个数、相关图书的价格总和、相关图书的平均价格、价格的最大值和价格的最小值
    NSLog(@"relative books price: %@",[book valueForKeyPath:@"relativeBooks.price"]);
    NSLog(@"relative books count: %@",[book valueForKeyPath:@"relativeBooks.@count"]);
    NSLog(@"relative books price sum: %@",[book valueForKeyPath:@"relativeBooks.@sum.price"]);
    NSLog(@"relative books price avg: %@",[book valueForKeyPath:@"relativeBooks.@avg.price"]);
    NSLog(@"relative books price max: %@",[book valueForKeyPath:@"relativeBooks.@max.price"]);
    NSLog(@"relative books price min: %@",[book valueForKeyPath:@"relativeBooks.@min.price"]);

    //另外,如果想获得没有重复的价格集合,可以这样:在使用@distinctUnionOfObjects后,发现效果是消除重复的价格。
    NSLog(@"relative books distinct price: %@",[book valueForKeyPath:@"relativeBooks.@distinctUnionOfObjects.price"]);
    
    
    // 示例6:更多用法
    // KVC还可以在一个语句中为实例的多个属性赋值:
    Book *book4=[[Book alloc] init];
    NSArray *bookProperties=[NSArray arrayWithObjects:@"name",@"price",nil];
    NSDictionary *bookPropertiesDictionary=[book4 dictionaryWithValuesForKeys:bookProperties];
    NSLog(@"book values: %@",bookPropertiesDictionary);
    
    NSDictionary *newBookPropertiesDictionary=[NSDictionary dictionaryWithObjectsAndKeys:@"《Objective C入门》",@"name", @"20.5",@"price",nil];
    [book4 setValuesForKeysWithDictionary:newBookPropertiesDictionary];
    NSLog(@"book with new values: %@",[book4 dictionaryWithValuesForKeys:bookProperties]);
    //另外,还有两个比较高级的内容:nil和覆盖setNilValueForKey方法,覆盖valueForUndefinedKey方法,可自行看reference了解。
}


0 0
原创粉丝点击