您的位置:首页 > 其它

OC学习——KVC KVO NSNotification

2015-08-06 14:12 519 查看
一、KVC

KVC(KeyValueCoding) “键-值-编码”是一种可以直接通过字符串的名字(key)来访问类实例变量的机制,是通过setter、getter方法访问。属性的访问和设置

KVC可以用来访问和设置实例变量的值。key是属性名称

设置方式:[self setValue:aName forKey:@"name"]

等同于:self.name = aName;

访问方式:aString = [self valueForKey:@"name"]

等同于:aString = self.name;

核心内容:

[对象 setValue aValue forKey aKey]; //对象的变量赋值

aValue = [对象 valueForKey aKey]; //把变量值取出来

for example:

1、设置属性值

//设置值

//这里setValue方法:第一个参数是value,第二个参数是key(就是类的属性名称)

[p setValue:@"jiangwei" forKey:@"name"];

Dog *dog = [[Dog alloc] init];

[p setValue:dog forKey:@"dog"];

使用setValue方法,就可以进行对属性进行设置值操作了,同时需要传递这个属性的名称,这个和Java中使用反射机制真的很像。

注:KVC设置值时,如果属性有set方法,则优先调用set方法,如果没有则直接设置上去,get方法一样

//设置基本数据类型&n
20000
bsp;

//这里需要将基本类型转化成NSNumber

//在设置值的时候,会有自动解包的过程,NSNumber会解包赋值给age

[p setValue:@22 forKey:@"age"];

还有一个需要注意的地方:当我们在设置基本类型的时候,需要将其转化成NSNumber类型的。

2、取属性的值

//读取值

NSString *name = [p valueForKey:@"name"];

取值就简单了

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

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

看个场景:

一个作者有多本书

Author.h

//

// Author.h

// 43_KeyValuePath

//

#import <Foundation/Foundation.h>

@interface Author : NSObject{

NSString *_name;

//作者出版的书,一个作者对应多个书籍对象

NSArray *_issueBook;

}

@end

作者类中定义了名字和一个书籍数组

Author.m

//

// Author.m

// 43_KeyValuePath

//

#import "Author.h"

@implementation Author

@end

Book.h

//

// Book.h

// 43_KeyValuePath

//

#import <Foundation/Foundation.h>

#import "Author.h"

@interface Book : NSObject{

Author *_author;

}

@property NSString *name;

@property floatfloat *price;

@end

定义了一个作者属性,书的名字,价格

Book.m

//

// Book.m

// 43_KeyValuePath

//

#import "Book.h"

@implementation Book

@end

看一下测试代码

main.m

[objc] view
plaincopy





//

// main.m

// 43_KeyValuePath

//

#import <Foundation/Foundation.h>

#import "Book.h"

#import "Author.h"

int main(int argc, const charchar * argv[]) {

@autoreleasepool {

//------------------KVC键值路径

/*

Book *book = [[Book alloc] init];

Author *author = [[Author alloc] init];

//设置作者

[book setValue:author forKey:@"author"];

//设置作者的名字

//路径为:author.name,中间用点号进行连接

[book setValue:@"jiangwei" forKeyPath:@"author.name"];

NSString *name = [author valueForKey:@"name"];

NSLog(@"name is %@",name);

*/

//--------------------KVC的运算

Author *author = [[Author alloc] init];

[author setValue:@"莫言" forKeyPath:@"name"];

Book *book1 = [[Book alloc] init];

book1.name = @"红高粱";

book1.price = 9;

Book *book2 = [[Book alloc] init];

book2.name = @"蛙";

book2.price = 10;

NSArray *array = [NSArray arrayWithObjects:book1,book2, nil nil];

[author setValue:array forKeyPath:@"issueBook"];

//基本数据类型会自动被包装成NSNumber,装到数组中

//得到所有书籍的价格

NSArray *priceArray = [author valueForKeyPath:@"issueBook.price"];

NSLog(@"%@",priceArray);

//获取数组的大小

NSNumber *count = [author valueForKeyPath:@"issueBook.@count"];

NSLog(@"count=%@",count);

//获取书籍价格的总和

NSNumber *sum = [author valueForKeyPath:@"issueBook.@sum.price"];

NSLog(@"%@",sum);

//获取书籍的平均值

NSNumber *avg = [author valueForKeyPath:@"issueBook.@avg.price"];

NSLog(@"%@",avg);

//获取书籍的价格最大值和最小值

NSNumber *max = [author valueForKeyPath:@"issueBook.@max.price"];

NSNumber *min = [author valueForKeyPath:@"issueBook.@min.price"];

}

return 0;

}

1、首先通过前面说到的KVC设置作者的书籍数组

//--------------------KVC的运算

Author *author = [[Author alloc] init];

[author setValue:@"莫言" forKeyPath:@"name"];

Book *book1 = [[Book alloc] init];

book1.name = @"红高粱";

book1.price = 9;

Book *book2 = [[Book alloc] init];

book2.name = @"蛙";

book2.price = 10;

NSArray *array = [NSArray arrayWithObjects:book1,book2, nil nil];

[author setValue:array forKeyPath:@"issueBook"];

添加了两本书籍

2、下面就开始用到KVC中键值路径了

1)获取作者类中书籍数组中所有书籍的价格

[java] view
plaincopy





//基本数据类型会自动被包装成NSNumber,装到数组中

//得到所有书籍的价格

NSArray *priceArray = [author valueForKeyPath:@"issueBook.price"];

NSLog(@"%@",priceArray);

看到了:@"issueBook.price" 这就是键值路径的使用,issueBook是作者类中的书籍数组属性名,price是书籍类的属性,中间用点号进行连接,这样我们就可以获取到了所有书籍的价格了,如果在Java中,我们需要用一个循环操作。但是OC中多么方便。

2)获取作者类中书籍数组的大小

//获取数组的大小

NSNumber *count = [author valueForKeyPath:@"issueBook.@count"];

NSLog(@"count=%@",count);

使用 @"issueBook.@count" 键值路径获取书籍数组的大小,issueBook是作者类中的书籍数组属性名,@count是特定一个写法,可以把它想象成一个方法,中间任然用点号进行连接

3)获取作者类中书籍数组的价格总和

//获取书籍价格的总和

NSNumber *sum = [author valueForKeyPath:@"issueBook.@sum.price"];

NSLog(@"%@",sum);

使用 @"issueBook.@sum.price" 键值路径获取书籍数组中的价格总和,issueBook是作者类中的书籍数组属性名,@sum是特性写法,可以把它想象成一个方法,price是书籍的价格属性名,可以把它看成是@sum的一个参数,中间用点号进行连接

如果在java中,这个需要用一个循环来计算总和,OC中很方便的

4)获取作者类中书籍数组的价格平均值、最小值、最大值

//获取书籍的平均值

NSNumber *avg = [author valueForKeyPath:@"issueBook.@avg.price"];

NSLog(@"%@",avg);

//获取书籍的价格最大值和最小值

NSNumber *max = [author valueForKeyPath:@"issueBook.@max.price"];

NSNumber *min = [author valueForKeyPath:@"issueBook.@min.price"];

操作和上面类似,这里就不解释了

我们看到上面返回来的数据都是NSNumber类型的

2、KVO 观察者

KVO(KeyValueObserver) “键-值-监听”定义了这样一种机制,当对象的属性值发生变化的时候,我们能收到一个“通知”。观察者更准确

NSObject提供了监听机制。所有子类也就全都能进行监听

KVO是基于KVC来实现的。

实现监听步骤

(1)注册监听对象。anObserver指监听者,keyPath就是要监听的属性值,而context方便传输你需要的数据,它是个指针类型。

其中, options是监听的选项,也就是说明监听返回的字典包含什么值。有两个常用的选项:

NSKeyValueObservingOptionNew 指返回的字典包含新值。

NSKeyValueObservingOptionOld 指返回的字典包含旧值。

(2)实现监听方法。监听方法在Value(属性的值)发生变化的时候自动调用。

其中,object指被监听的对象。change里存储了一些变化的数据,比如变化前的数据,变化后的数据。

KVO操作在OC中也是经常会用到的,而且这种机制在java中不存在的。

它的作用就是用来监听类中属性值的变化,实现原理是观察者模式,当然我们也可以使用观察者模式在Java中实现这样的机制

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

Chidren.h

[objc] view
plaincopy





//

// Children.h

// 44_KVO

//

// Created by jiangwei on 14-10-16.

// Copyright (c) 2014年 jiangwei. All rights reserved.

//

#import <Foundation/Foundation.h>

@interface Children : NSObject

@property NSInteger *hapyValue;

@property NSInteger *hurryValue;

@end

Children.m

[objc] view
plaincopy





//

// Children.m

// 44_KVO

//

// Created by jiangwei on 14-10-16.

// Copyright (c) 2014年 jiangwei. All rights reserved.

//

#import "Children.h"

@implementation Children

- (id) init{

self = [super init];

if(self != nil){

//启动定时器

[NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(timerAction) userInfo:nil repeats:YES];

self.hapyValue= 100;

}

return self;

}

- (void) timerAction:(NSTimer *) timer{

//使用set方法修改属性值,才能触发KVO

int value = _hapyValue;

[self setHapyValue:--value];

int values = _hurryValue;

[self setHurryValue:--values];

}

@end

在初始化方法中,我们启动一个定时器,然后隔1s就去修改孩子类的值

Nure.h

[objc] view
plaincopy





//

// Nure.h

// 44_KVO

//

// Created by jiangwei on 14-10-16.

// Copyright (c) 2014年 jiangwei. All rights reserved.

//

#import <Foundation/Foundation.h>

@class Children;

@interface Nure : NSObject{

Children *_children;

}

- (id) initWithChildren:(Children *)children;

@end

定义一个孩子属性

Nure.m

[objc] view
plaincopy





//

// Nure.m

// 44_KVO

//

// Created by jiangwei on 14-10-16.

// Copyright (c) 2014年 jiangwei. All rights reserved.

//

#import "Nure.h"

#import "Children.h"

@implementation Nure

- (id) initWithChildren:(Children *)children{

self = [super init];

if(self != nil){

_children = children;

//观察小孩的hapyValue

//使用KVO为_children对象添加一个观察者,用于观察监听hapyValue属性值是否被修改

[_children addObserver:self forKeyPath:@"hapyValue" 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 *)change context:(voidvoid *)context{

NSLog(@"%@",change);

//通过打印change,我们可以看到对应的key

//通过keyPath来判断不同属性的观察者

if([keyPath isEqualToString:@"hapyValue"]){

//这里change中有old和new的值是因为我们在调用addObserver方法时,用到了

//NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld;想要哪一个就用哪一个

//[change objectForKey:@"old"]是修改前的值

NSNumber *hapyValue = [change objectForKey:@"new"];//修改之后的最新值

NSInteger *value = [hapyValue integerValue];

if(value < 90){

//do something...

}

}else if([keyPath isEqualToString:@"hurryValue"]){

//这里change中有old和new的值是因为我们在调用addObserver方法时,用到了

//NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld;想要哪一个就用哪一个

//[change objectForKey:@"old"]是修改前的值

NSNumber *hurryValue = [change objectForKey:@"new"];//修改之后的最新值

NSInteger *value = [hurryValue integerValue];

if(value < 90){

//do something...

}

}

NSLog(@"%@",context);//打印的就是addObserver方法的context参数

//使用KVC去修改属性的值,也会触发事件

}

- (void)dealloc{

//移除观察者

[_children removeObserver:self forKeyPath:@"hapyValue"];

[_children removeObserver:self forKeyPath:@"hurryValue"];

}

@end

看到了在这里就开始进行监听操作了

下面来具体看一下如何做到监听的

1、添加监听对象

我们使用addObserver方法给孩子添加监听对象

第一个参数:监听者,这里是Nure,所以可以直接传递self

第二个参数:监听对象的属性名

第三个参数:监听这个属性的状态:这里可以使用|进行多种组合操作,属性的新值和旧值

第四个参数:传递内容给监听方法

[java] view
plaincopy





//观察小孩的hapyValue

//使用KVO为_children对象添加一个观察者,用于观察监听hapyValue属性值是否被修改

[_children addObserver:self forKeyPath:@"hapyValue" options:NSKeyValueObservingOptionNew |NSKeyValueObservingOptionOld context:@"context"];

//观察小孩的hurryValue

[_children addObserver:self forKeyPath:@"hurryValue" options:NSKeyValueObservingOptionNew |NSKeyValueObservingOptionOld context:@"context"];

2、监听方法

[objc] view
plaincopy





//触发方法

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(voidvoid *)context{

NSLog(@"%@",change);

//通过打印change,我们可以看到对应的key

//通过keyPath来判断不同属性的观察者

if([keyPath isEqualToString:@"hapyValue"]){

//这里change中有old和new的值是因为我们在调用addObserver方法时,用到了

//NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld;想要哪一个就用哪一个

//[change objectForKey:@"old"]是修改前的值

NSNumber *hapyValue = [change objectForKey:@"new"];//修改之后的最新值

NSInteger *value = [hapyValue integerValue];

if(value < 90){

//do something...

}

}else if([keyPath isEqualToString:@"hurryValue"]){

//这里change中有old和new的值是因为我们在调用addObserver方法时,用到了

//NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld;想要哪一个就用哪一个

//[change objectForKey:@"old"]是修改前的值

NSNumber *hurryValue = [change objectForKey:@"new"];//修改之后的最新值

NSInteger *value = [hurryValue integerValue];

if(value < 90){

//do something...

}

}

NSLog(@"%@",context);//打印的就是addObserver方法的context参数

//使用KVC去修改属性的值,也会触发事件

}

我们上面传递的第一个参数是监听者,这个方法也是在监听者中实现的,当属性值发生变化的时候,这个方法会被回调

这个方法的参数:

第一个参数:键值路径

第二个参数:监听对象

第三个参数:变化的值

第四个参数:传递的内容

我们看到代码中有一个特殊的参数:第三个参数:NSDirctionary类型的

其实我们如果不知道是干什么的,我们可以打印一下他的结果看一下,很简单,这里就不截图说明了

我们会发现他有两个键值对

key是:new和old

他们就是分别代表这个属性值变化的前后值,同时他们的得到也和之前我们添加监听对象时设置的第三个参数有关:

NSKeyValueObservingOptionNew |NSKeyValueObservingOptionOld
那个地方设置了几种状态,这里的NSDirctionary中就会有几个键值对

3、销毁方法

这个并不属于KVO的内容了,只是在这里用到了就顺便说一下

[objc] view
plaincopy





- (void)dealloc{

//移除观察者

[_children removeObserver:self forKeyPath:@"hapyValue"];

[_children removeObserver:self forKeyPath:@"hurryValue"];

}

我们在创建一个对象的时候会调用alloc方法,当对象被销毁的时候会调用dealloc这个方法,这个和C++中的析构函数一样,Java中有垃圾回收器,所以没有此类的方法,但是有一个finalize方法,其实这个方法就是在垃圾回收器回收对象的时候会调用,和这个功能差不多,但是在Java中,我们并不提倡使用这个方法。因为会造成GC的回收发生错误。

我们在销毁方法中需要移除监听者

总结

这一篇就介绍了OC中比较有特色的两个机制:KVC和KVO

KVC:就是可以暴力的去get/set类的私有属性,同时还有强大的键值路径对数组类型的属性进行操作

KVO:监听类中属性值变化的

三、通知
通知是iOS开发框架中的一种设计模式,内部的实现机制由Cocoa框架支持。通知一般用于M、V、C的间的信息传递。像我在设置页面设置App皮肤。

M是modol模型 V是view视图 C是control控制器。

系统通知

//注册通知

其中,selector是方法名 class是描述类的类 SEL
method=@selector(方法名)

通知用完要移除
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  KVC KVO NSNotification