KVC 和 KVO

1. KVC(key value coding)

1.1. 基本概念

  1> 我们一般是通过set方法或者属性的点语法来直接更改对象的状态,即对象的属性值,比如[stu setAge:29];   stu.age = 30;

  2> KVC 它是以间接更改对象状态的方式,其实现方法是使用字符串来描述对象需要更改的属性。KVC中经常调用的方法包括valueForKey:和setVale:forKey:,以字符串的方式想对象发送消息。

1.2. 键路径(Key Path)

  1> 除了通过键设置值外,KVC还支持键路径,像文件系统路径一样,其实就是属性的链式访问。

  2> 比如:利用键路径设置Student对象中Card对象的no属性,语法形式如下:

    [student setValue:@"123456" forKeyPath:@"card.no"];

  3> 获取Student对象中Card对象的no,其语法形式如下:

    [student valueForKeyPath:@"card.no"];

1.3. 数组的整体操作

  1> 如果向一个NSArray请求一个Key,KVC会查找数组中每一个对象来查找Key,然后将查询结果打包到一个数组中并返回。

    例如:Student里面有很多book对象

    @interface Book : NSObject

    @property (nonatomic,copy) NSString *name;

    @property (nonatomic,assign) CGFloat price;

    @end

    @interface Student : NSObject

    @property (nonatomic,strong) NSArray *books;

    @end

    要获取Student中所有Book的name ,语法形式如下:

    NSArray *names = [student.books valueForKeyPath:@"name"];

    或者

    NSArray *names = [student valuesForKeyPath:@"books.name"];

    注意:不能在键路径中为数组添加索引,比如:@"book[0].name".

1.4. 键路径运算符

  1> 在键路径中,可以引用一些运算符来竞选一些运算,例如获取一些值平均数、最小数、最大数、总数等。以下举例:

    1) 计算Student中Book对象的总数

      NSNumber *count = [student.books valueForKeyPath:@"@count"];

      或者

      NSNumber *count = [student valueForKeyPath:@"books.@count"];

    2) 计算Student中所有Book的价钱(price)总和

      NSNumber *sum = [student.books valueForKeyPath:@"@sum.price"];

      或者

      NSNumber *sum = [student valueForKeyPath:@"books.@sum.price"];

    3) 找出Student中Book的所有不同价位(排除相同价位)

      NSArray *prices = [student.books valueForKeyPath:@"@distinctUnionOfObjects.price"];

      或者

      NSArray *prices = [student valueForKeyPath:@"books.@distinctUnionOfObjects.price"];

2. KVO(key value observing)

  1> KVO是一种非常重要的机制,它允许监听对象的属性的变化

  2> 注册监听器方法,如有形式如下:

    -(void)addObserver:(NSObject*)anObjecter //监听器对象

        forKeyPath:(NSString *) keyPath //监听的属性

        options:(NSKeyValueObservingOptions)options //决定当前属性变化时,要传什么数据给监听器

        context:(void *)context;

  3> 监听器需要实现监听方法,语法形式如下:

    -(void)observeValueForKeyPath:(NSString *)keyPath //监听的属性

        ofObject:(id)object //谁的属性改变了

        change:(NSDictionary*)change //属性改变时传过来的信息,(取决于添加监听器时option参数)

        context:(void*)context;

  4> 移除监听器,其语法形式如下:

    -(void)removeObserver:(NSObject*)observer forKeyPath:(NSString*)keyPath;

  

  【示例 - KVC && KVO】 

/** Card 身份证 **/
#import <Foundation/Foundation.h>
@interface Card : NSObject
@property (strong,nonatomic) NSString *cardNo;
@end

#import "Card.h"
@implementation Card
- (NSString *)description
{
    return [NSString stringWithFormat:@"<Card:%p cardNo=%@>", self,self.cardNo];
}
@end

 


/** Book 书本 **/
#import <Foundation/Foundation.h>
@interface Book : NSObject
//书名
@property (nonatomic,strong) NSString *bookName;
//价格
@property (nonatomic,assign) CGFloat price;
@end

#import "Book.h"
@implementation Book
- (NSString *)description
{
    return [NSString stringWithFormat:@"<Book:%p bookName=%@ price=%f>", self,self.bookName,self.price];
}
@end
/** Person 人 **/

#import <Foundation/Foundation.h>
#import "Card.h"
@interface Person : NSObject
//姓名
@property (nonatomic,strong) NSString *name;
//年龄
@property (nonatomic,assign) NSInteger age;
//身份证
@property (nonatomic,strong) Card *card;
//书本
@property (nonatomic,strong) NSArray *books;
@end

#import "Person.h"
@implementation Person
- (NSString *)description
{
    return [NSString stringWithFormat:@"<Person:%p name=%@ age=%d card=%@ books=%@>", self,self.name,self.age,self.card,self.books];
}
@end
/** 控制器类 **/

#import <UIKit/UIKit.h>
@interface MainViewController : UIViewController
@end

#import "MainViewController.h"
#import "Person.h"
#import "Book.h"
@implementation MainViewController

-(void)viewDidLoad{
    [super viewDidLoad];
    
    UIButton *button = [UIButton buttonWithType:UIButtonTypeRoundedRect];
    [button setTitle:@"测试" forState:UIControlStateNormal];
    [button setFrame:CGRectMake(60, 100, 200, 50)];
    [button addTarget:self action:@selector(testMethod1) forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview:button];
    
    UIButton *button2 = [UIButton buttonWithType:UIButtonTypeRoundedRect];
    [button2 setTitle:@"Plist测试" forState:UIControlStateNormal];
    [button2 setFrame:CGRectMake(60, 150, 200, 100)];
    [button2 addTarget:self action:@selector(testMethod2) forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview:button2];
    
    UIButton *button3 = [UIButton buttonWithType:UIButtonTypeRoundedRect];
    [button3 setTitle:@"KVO测试" forState:UIControlStateNormal];
    [button3 setFrame:CGRectMake(60, 200, 200, 100)];
    [button3 addTarget:self action:@selector(kvoTest) forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview:button3];
  }

-(void) testMethod1{
    Person *person = [[Person alloc]init];

    //设置Person自身属性
    [person setValue:@"yyy" forKeyPath:@"name"];
    [person setValue:@20 forKeyPath:@"age"];
    
    //设置Person.Card属性
    person.card = [[Card alloc]init];
    [person setValue:@"342225" forKeyPath:@"card.cardNo"];
    
    Book *book1 = [[Book alloc]init];
    book1.bookName = @"IOS 1";
    book1.price = 28.98;
    
    Book *book2 = [[Book alloc]init];
    book2.bookName = @"IOS 2";
    book2.price = 38.98;
    
    person.books = @[book1,book2];
    
    NSLog(@"%@",person);
    NSLog(@"booksName = %@",[person valueForKeyPath:@"books.bookName"]);
}

-(void) testMethod2{
    NSString *path = [[NSBundle mainBundle]pathForResource:@"persons" ofType:@"plist"];
    NSArray *array = [NSArray arrayWithContentsOfFile:path];
    
    NSMutableArray *persons = [NSMutableArray arrayWithCapacity:array.count];
    
    for (NSDictionary *dic in array) {
        Person *p = [[Person alloc]init];
        [p setValuesForKeysWithDictionary:dic];
        [persons addObject:p];
    }
    NSLog(@"persons = %@",persons);
}

-(void) kvoTest{
    Person *p = [[Person alloc]init];
    p.card = [[Card alloc]init];
    p.name = @"wyp";
    
    [p addObserver:self forKeyPath:@"card.cardNo" options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld context:nil];
    
    p.card.cardNo = @"123";
    p.age = 20;
    
    [p removeObserver:self forKeyPath:@"card.cardNo"];
    
    p.card.cardNo = @"qqqqq";
}

-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context{
    NSLog(@"%@ %@ %@ %@",keyPath,object,change,context);
}
@end

 

/** plist文件 **/

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<array>
    <dict>
        <key>name</key>
        <string>wyp</string>
        <key>age</key>
        <integer>26</integer>
        <key>card</key>
        <dict>
            <key>cardNo</key>
            <string>34222519881229445X</string>
        </dict>
        <key>books</key>
        <array>
            <dict>
                <key>bookName</key>
                <string>IOS 1</string>
                <key>price</key>
                <real>28.98</real>
            </dict>
            <dict>
                <key>bookName</key>
                <string>IOS 2</string>
                <key>price</key>
                <real>38.98</real>
            </dict>
        </array>
    </dict>
    <dict>
        <key>name</key>
        <string>qlp</string>
        <key>age</key>
        <integer>26</integer>
        <key>card</key>
        <dict>
            <key>cardNo</key>
            <string>34222519891006374X</string>
        </dict>
        <key>books</key>
        <array>
            <dict>
                <key>bookName</key>
                <string>PS 1</string>
                <key>price</key>
                <real>28.98</real>
            </dict>
            <dict>
                <key>bookName</key>
                <string>PS 2</string>
                <key>price</key>
                <real>38.98</real>
            </dict>
        </array>
    </dict>
</array>
</plist>

posted on 2015-02-17 19:52  雾里寻梦  阅读(273)  评论(0)    收藏  举报

导航