KVC和KVO操作

来源:互联网 发布:idea 测试java代码 编辑:程序博客网 时间:2024/06/05 07:28
一、KVC概述

KVC是KeyValueCoding的简称,它是一种可以直接通过字符串的名字(key)来访问类属性(实例变量)的机制。而不是通过调用Setter、Getter方法访问。(和Java中是使用反射机制去访问类的private权限的变量类似,很暴力的.这样做就会破坏类的封装性!)当使用KVO、Core Data、CocoaBindings、AppleScript(Mac支持)时,KVC是关键技术。

二,使用方法
关键方法定义在:NSKeyValueCodingprotocol

KVC支持类对象和内建基本数据类型。

获取值

valueForKey:,传入NSString属性的名字。

valueForKeyPath:,传入NSString属性的路径,xx.xx形式。

valueForUndefinedKey它的默认实现是抛出异常,可以重写这个函数做错误处理。


修改值

setValue:forKey:

setValue:forKeyPath:

setValue:forUndefinedKey:

setNilValueForKey:当对非类对象属性设置nil时,调用,默认抛出异常。

一对多关系成员的情况

mutableArrayValueForKey:有序一对多关系成员  NSArray

mutableSetValueForKey:无序一对多关系成员  NSSet


实例

Person.h

#import <Foundation/Foundation.h>#import "Dog.h"@interface Person : NSObject{    @private    NSString *_name;    NSInteger _age;    Dog *_dog;}- (void)setName:(NSString *)name;@end

Person.m

#import "Person.h"@implementation Person- (void)setName:(NSString *)name{    _name = name;    NSLog(@"KVC在赋值是会优先调用setter方法");}- (NSString *)description{    return [NSString stringWithFormat:@"name =  %@,dog = %@, age = %ld, dog.weight = %.2f", _name, _dog, _age, _dog.weight];}@end


Dog.h

#import <Foundation/Foundation.h>@interface Dog : NSObject@property (nonatomic, assign) double weight;@end

Dog.m

#import "Dog.h"@implementation Dog@end

Main.m

#import <Foundation/Foundation.h>#import "Person.h"#import "Dog.h"/** KVC:即使一个类的属性是私有的,而且也没有setter/getter方法,同样可以读写. 很暴力 相当于JAVA中的反射,破坏类的封装性 */int main(int argc, const char * argv[]) {    @autoreleasepool {        Person *p = [[Person alloc] init];                Dog *d = [[Dog alloc] init];                //设置值        //KVC设置值时,如果属性中有setter方法,则优先调用setter方法,如果没有则直接设置上去,getter方法类似        [p setValue:@"ly" forKey:@"name"];                //如果在设置Value时,没有对应的key,程序就会崩溃        [p setValue:d forKeyPath:@"dog"];                //[p setValue:@"0.5" forKey:@"dog.weight"];(错误)        [p setValue:@"0.5" forKeyPath:@"dog.weight"];                //设置基本数据类型        //这里需要将基本类型转化成NSNumber,在设置值的时候,会有自动解包,NSNumber会解包赋值给age        [p setValue:@23 forKeyPath:@"_age"];                NSDictionary *personDict = @{@"name" : @"sunny",                                     @"age" : @"22",                                     @"dog" : [[Dog alloc] init]};                Person *person = [[Person alloc] init];        // 字典转模型:setValuesForKeysWithDictionary        // 1>必须字典中对应的key和模型中对应的属性是一致        // 2>字典中存在的属性,在模型中必须有对应的属性        [person setValuesForKeysWithDictionary:personDict];                NSLog(@"person:%@", person);                //读取值        NSString *name = [p valueForKey:@"_name"];        NSLog(@"%@",name);                NSLog(@"%@",p);                    }    return 0;}

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

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

Author.h

#import <Foundation/Foundation.h>@interface Author : NSObject{    NSString *_name;    //一个作者对应多个出版书籍    NSArray *_issueBook;}@end

Author.m

#import "Author.h"@implementation Author@end

Book.h

#import <Foundation/Foundation.h>#import "Author.h"@interface Book : NSObject{    Author *_author;}@property NSString *name;@property float price;@end

Book.m

#import "Book.h"@implementation Book@end

Main.m

#import <Foundation/Foundation.h>#import "Author.h"#import "Book.h"/** 键值路径是对于一个类中有数组对象的属性进行便捷操作;  */int main(int argc, const char * argv[]) {    @autoreleasepool {        Author *author = [[Author alloc] init];        [author setValue:@"ly" forKey:@"name"];                Book *book1 = [[Book alloc] init];                book1.name = @"iOS开发进阶";        book1.price = 98.00f;        Book *book2 = [[Book alloc] init];        book2.name = @"Android内核解析";        book2.price =88.00f;                NSArray *books = [NSArray arrayWithObjects:book1, book2, nil];                [author setValue:books forKey:@"issueBook"];                //基本数据类型会自动装箱成NSNumber,装到数组中;        //得到所有书籍的价格        NSArray *prices = [author valueForKeyPath:@"issueBook.price"];        NSLog(@"prices = %@", prices);                //获取数组的大小        NSNumber *count = [author valueForKeyPath:@"issueBook.@count"];        NSLog(@"count = %@", count);                //获取书籍价格的总和        NSNumber *priceSum = [author valueForKeyPath:@"issueBook.@sum.price"];        NSLog(@"priceSum = %@", priceSum);                //获取书籍价格的平均值        NSNumber *priceAvg = [author valueForKeyPath:@"issueBook.@avg.price"];        NSLog(@"priceAvg = %@", priceAvg);                //获取书籍价格的最大值和最小值        NSNumber *max = [author valueForKeyPath:@"issueBook.@max.price"];        NSNumber *min = [author valueForKeyPath:@"issueBook.@min.price"];        NSLog(@"max = %@, min = %@", max, min);                            }    return 0;}

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

1. 注册,指定被观察者的属性,
2. 实现回调方法
3. 移除观察

三,实例:

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

Children.h

#import <Foundation/Foundation.h>/** 定义一个小孩类,它由两个属性,开心值和饥饿值 */@interface Children : NSObject@property (nonatomic, assign) NSInteger happyValue;@property (nonatomic, assign) NSInteger hurryValue;@end

Children.m

#import "Children.h"@implementation Children- (instancetype)init{    self = [super init];    if (self) {        //启动定时器 每隔一秒去修改孩子类的值        [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(timerAction:) userInfo:nil repeats:YES];        self.happyValue = 100;    }    return self;}- (void)timerAction:(NSTimer *)timer{    //使用setter方法修改属性值才能触发KVO        self.hurryValue = --_hurryValue;        self.happyValue = --_happyValue;    }@end

Nure.h

#import <Foundation/Foundation.h>@class Children;@interface Nure : NSObject@property (nonatomic, strong) Children *children;- (instancetype)initWithChildren:(Children *)children;@end

Nure.m

#import "Nure.h"#import "Children.h"@implementation Nure- (instancetype)initWithChildren:(Children *)children{    if (self = [super init]) {        _children = children;                //观察孩子的happyValue        //使用KVO为_children对象添加一个观察者,用于观察监听happyValue属性值是否被修改        [_children addObserver:self forKeyPath:@"happyValue" options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld context:@"context"];                //观察孩子的hurryValue        [_children addObserver:self forKeyPath:@"hurryValue" options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld context:@"context"];                    }    return self;}//触发方法- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context{    //通过打印输出change,可以看到对应的key    NSLog(@"%@",change);    //通过keyPath来判断不同属性的观察者    if ([keyPath isEqualToString:@"happyValue"]) {        //这里change中有old和new的值是因为在调用addObserver方法时,用到了        //options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld        NSNumber *happyValue = [change objectForKey:@"new"];//修改之后的最新值                NSInteger value = [happyValue integerValue];        if (value < 80) {            NSLog(@"孩子的开心值小于90");        }    }else if ([keyPath isEqualToString:@"hurryValue"])    {        NSNumber *hurryValue = [change objectForKey:@"new"];//修改之后的最新值                NSInteger value = [hurryValue integerValue];                if (value < 0) {            NSLog(@"孩子的饥饿值小于0");        }    }        //打印addObserver方法的context参数    NSLog(@"%@", context);        //使用KVO去修改属性的值,也会触发事件    }- (void)dealloc{    //移除观察者(这个并不属于KVO的内容)    [_children removeObserver:self forKeyPath:@"happyValue"];    [_children removeObserver:self forKeyPath:@"hurryValue"];}@end

Main.m

#import <Foundation/Foundation.h>#import "Children.h"#import "Nure.h"/** KVO 这种机制在JAVA中是不存在的 它的作用就是用来监听类中属性值的变化,实现原理是观察者模式,当然我们也可以使用观察者模式在java中实现这样的机制 */int main(int argc, const char * argv[]) {    @autoreleasepool {        Children *children = [[Children alloc] init];                Nure *nure = [[Nure alloc] initWithChildren:children];                      //启动        [[NSRunLoop currentRunLoop] run];            }    return 0;}

小结:

KVO/KVC这种编码方式使用起来很简单,很适用与datamodel修改后,引发的UIVIew的变化这种情况,就像上边的例子那样,当更改属性的值后,监听对象会立即得到通知。

1 0