06 KVC与KVO

06 KVC与KVO

Tags:Objective-C


6.1 KVC

KVC(Key Value Coding):键值编码,是一种修改对象的属性和访问对象的属性的技术,当然我们也可以用 对象名->成员变量名 或者 对象名.属性名 来修改或者访问属性。

下面贴个代码例子:
GKHBook.h

#import <Foundation/Foundation.h>

@interface GKHBook : NSObject

@property (nonatomic, copy) NSString *bookName;//书名
@property (nonatomic, assign) int price;//价格
@end

GKHBook.m

#import "GKHBook.h"

@implementation GKHBook

@end

GKHUser.h

#import <Foundation/Foundation.h>
#import "GKHBook.h"

@interface GKHUser : NSObject {
    @package
    NSString *habit;//爱好
    NSString *_habit;

}

@property (nonatomic, copy) NSString *name;//姓名
@property (nonatomic, copy) NSString *pass;//密码
@property (nonatomic, copy) NSDate *birth;//生日
@property (nonatomic, assign) int teethNumber;//牙齿数
@property (nonatomic, strong) GKHBook *book;//购买的书
@property (nonatomic, assign) int bookAmount;//购买的书的数量

@end

GKHUser.m

#import "GKHUser.h"

@implementation GKHUser {
    int age; //年龄
}

//重写setValue:forUndefinedKey:方法
- (void) setValue:(id)value forUndefinedKey:(NSString *)key {
    NSLog(@"你尝试设置的key:%@ 并不存在", key);
    NSLog(@"你尝试设置的value为:%@", value);
}

//重写valueForUndefinedKey:方法
 - (void) valueForUndefinedKey:(id)key {
 NSLog(@"你尝试访问的key:%@ 并不存在", key);
 }

//重写setNilValueForKey:方法,可以对不同的key做不同的处理
- (void) setNilValueForKey:(NSString *)key {
    //如果key为teethNumber的属性值设置为nil
    if ([key isEqualToString:@"teethNumber"]) {
        //nil代表0
        self.teethNumber = 0;
    }
    else {
        //调用父类的setNilValueForKey:方法
        [super setNilValueForKey:key];
    }
}

@end

main.m

#import <Foundation/Foundation.h>
#import "GKHUser.h"

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        GKHUser *user = [[GKHUser alloc] init];
        //使用KVC方式为name、pass、birth属性设置属性值
        [user setValue:@"孙悟空" forKey:@"name"];
        [user setValue:@"123456" forKey:@"pass"];
        [user setValue:[[NSDate alloc] init] forKey:@"birth"];

        //使用KVC方式访问name、pass、birth属性值
        NSLog(@"user的name属性为:%@", [user valueForKey:@"name"]);
        NSLog(@"user的pass属性为:%@", [user valueForKey:@"pass"]);
        NSLog(@"user的birth属性为:%@", [user valueForKey:@"birth"]);

        //使用KVC方式为为属性赋值,KVC对属性赋值的搜索顺序是:
        //①set属性名: 方法 ②_属性名 成员变量 ③属性名 成员变量  在②③中不管成员变量的修饰符是什么也不论其在interface部分还是implement部分
        //④如果①②③都没找到,系统将会调用对象的setVale:forUndefinedKey:方法,该方法将引起一NSUnknownKeyException异常导致程序结束
        [user setValue:@"football" forKey:@"habit"];
        [user setValue:[NSNumber numberWithInt:18] forKey:@"age"];

        //使用KVC方式访问属性值,搜索顺序是:
        //①属性名 方法 ②_属性名 成员变量 ③属性名 成员变量  在②③中不管成员变量的修饰符是什么也不论其在interface部分还是implement部分
        //④如果①②③都没找到,系统将会调用对象的valueforUndefinedKey:方法,该方法将引起一个NSUnknownKeyException异常导致程序结束
        NSLog(@"user->habit:%@", user->habit);//null
        NSLog(@"user->_habit:%@", user->_habit);//football
        NSLog(@"user的age为:%@", [user valueForKey:@"age"]);//18

        //当对一些属性设置为nil时,如对NSString类型的name是合法的,但是一些不接受nil值的属性,比如对int类型的teethNumber设置为nil
        //将会调用系统的setNilValueForKey:方法,该方法将引起一个NSInvalidArgumentException异常
        //重写setNilValueForKey:方法对一些不接受nil类型的key做处理,这里对对teethNumber设置为nil将变为0
        [user setValue:nil forKey:@"teethNumber"];
        NSLog(@"user的teethNumber属性为:%@", [user valueForKey:@"teethNumber"]);

        //使用KVC方式设置复合属性,这里对book属性下的price和bookName赋值
        [user setValue:[[GKHBook alloc] init]forKey:@"book"];
        [user setValue:@"20" forKeyPath:@"book.price"];
        [user setValue:@"《iOS编程》" forKeyPath:@"book.bookName"];
        [user setValue:@"30" forKey:@"bookAmount"];
        NSLog(@"user订了书名为:%@,价格为:%@,数量为:%@", [user valueForKeyPath:@"book.bookName"],
              [user valueForKeyPath:@"book.price"], [user valueForKey:@"bookAmount"]);

        //使用KVC方式为不存在的属性赋值,如果我们不重写继承自NSObject的setVale:forUndefinedKey:方法将导致程序异常
        //这里我们在GKHUser.m中重写了setVale:forUndefinedKey:方法
        [user setValue:@"red" forKey:@"color"];
        //使用KVC方式访问不存在的属性,如果我们不重写继承自NSObject的valueForUndefinedKey:方法将导致程序异常
        //这里我们在GKHUser.m中重写了valueForUndefinedKey:方法
        NSLog(@"user的color属性为:%@", [user valueForKey:@"color"]);
    }
    return 0;
}

运行结果:

user的name属性为:孙悟空
user的pass属性为:123456
user的birth属性为:2015-11-30 09:59:38 +0000
user->habit:(null)
user->_habit:football
user的age为:18
user的teethNumber属性为:0
user订了书名为:《iOS编程》,价格为:20,数量为:30
你尝试设置的key:color 并不存在
你尝试设置的value为:red
你尝试访问的key:color 并不存在

6.2 KVO

KVO(Key Value Observing):键值监听。它提供一种机制,当指定的对象的属性被修改后,则对象就会接受到通知。简单的说就是每次指定的被观察的对象的属性被修改后,KVO就会自动通知相应的观察者了。
一般实现步骤分为三步:
1. 添加监听器,并将被观察的对象中需要监视的属性注册到监听器,通过addObserver:forKeyPath:options:context:方法设置。
2. 实现回调方法,通过重写继承自NSObject对象的observeValueForKeyPath:ofObject:change:context:方法,来实现属性发生修改后要完成的功能。
3. 删除监听器,在观察者对象中delloc方法调用removeObserver:forKeyPath来删除每一个属性的监听器。

下面有个例子,代码中GKHBook类还是6.1中一样,其余代码如下:
GKHBookView.h

#import <Foundation/Foundation.h>
#import "GKHBook.h"

@interface GKHBookView : NSObject

@property (nonatomic, weak) GKHBook *book;
- (void) showBookInfo;
@end

GKHBookView.m

/*
 GKHBook是数据模型组件,包含一些数据,具有属性
 GKHBookVeiw是视图模型组件,包含需要使用的数据模型,当数据模型组件发生属性变化时,能通知视图模型组件
 */
#import "GKHBookView.h"

@implementation GKHBookView

- (void) showBookInfo {
    NSLog(@"book名为:%@,价格为:%d", self.book.bookName, self.book.price);
}

//自定义setBook:方法
- (void) setBook:(GKHBook *)book {
    self->_book = book;
    //在self上为book添加监听器,监听book的bookName属性的改变
    [self.book addObserver:self forKeyPath:@"bookName" options:NSKeyValueObservingOptionNew context:nil];
    //在self上为book添加监听器,监听book的price属性的改变
    [self.book addObserver:self forKeyPath:@"price" options:NSKeyValueObservingOptionNew context:nil];
}

//重写observeValueForKeyPath:方法,当被监听的数据模型发生改变时,就会回调监听器的该方法
- (void) observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context {
    NSLog(@"--observeValueForKeyPath:方法被调用--");
    //获取属性被修改时的数据
    NSLog(@"被修改的keyPath为:%@", keyPath);
    NSLog(@"被修改的对象为:%@", object);
    NSLog(@"新被修改的属性值为:%@", [change objectForKey:@"new"]);
    NSLog(@"被修改的上下文为:%@", context);
}

- (void) delloc {
    //删除self上的监听器
    [self.book removeObserver:self forKeyPath:@"bookName"];
    [self.book removeObserver:self forKeyPath:@"price"];
}
@end

main.m

#import <Foundation/Foundation.h>
#import "GKHBookView.h"

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        //创建GKHBook对象
        GKHBook *book = [[GKHBook alloc] init];
        //设置book的属性bookName price
        book.bookName = @"《iOS编程》";
        book.price = 89;
        //创建GKHBookView对象
        GKHBookView *view = [[GKHBookView alloc] init];
        view.book = book;
        [view showBookInfo];
        //修改book对象的属性,将会激发view中的监听器方法
        book.bookName = @"《iOS编程第四版》";
        book.price = 99;
    }
    return 0;
}

运行结果为:

book名为:《iOS编程》,价格为:89
–observeValueForKeyPath:方法被调用–
被修改的keyPath为:bookName
被修改的对象为:
新被修改的属性值为:《iOS编程第四版》
被修改的上下文为:(null)
–observeValueForKeyPath:方法被调用–
被修改的keyPath为:price
被修改的对象为:
新被修改的属性值为:99
被修改的上下文为:(null)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值