KVC和KVO操作

一、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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值