一、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、设置属性值
-
-
- [p setValue:@"jiangwei" forKey:@"name"];
-
- Dog *dog = [[Dog alloc] init];
- [p setValue:dog forKey:@"dog"];
使用setValue方法,就可以进行对属性进行设置值操作了,同时需要传递这个属性的名称,这个和Java中使用反射机制真的很像。
注:KVC设置值时,如果属性有set方法,则优先调用set方法,如果没有则直接设置上去,get方法一样
-
-
-
- [p setValue:@22 forKey:@"age"];
还有一个需要注意的地方:当我们在设置基本类型的时候,需要将其转化成NSNumber类型的。
2、取属性的值
-
- NSString *name = [p valueForKey:@"name"];
取值就简单了
下面再来看一下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 floatfloat *price;
-
- @end
定义了一个作者属性,书的名字,价格
Book.m
-
-
-
-
-
- #import "Book.h"
-
- @implementation Book
-
- @end
看一下测试代码
main.m
-
-
-
-
-
- #import <Foundation/Foundation.h>
- #import "Book.h"
- #import "Author.h"
-
- int main(int argc, const charchar * argv[]) {
- @autoreleasepool {
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 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"];
-
-
-
- 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设置作者的书籍数组
-
- 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)获取作者类中书籍数组中所有书籍的价格
-
-
- 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是监听的选项,也就是说明监听返回的字典包含什么值。有两个常用的选项:
NSKeyValueObservingOptio
nNew 指返回的字典包含新值。
NSKeyValueObservingOptio
nOld
指返回的字典包含旧值。
(2)实现监听方法。监听方法在Value(属性的值)发生变化的时候自动调用。
其中,object指被监听的对象。change里存储了一些变化的数据,比如变化前的数据,变化后的数据。
KVO操作在OC中也是经常会用到的,而且这种机制在java中不存在的。
它的作用就是用来监听类中属性值的变化,实现原理是观察者模式,当然我们也可以使用观察者模式在Java中实现这样的机制
看一下具体的例子:现在有一个小孩类,他有两个属性:开心值,饥饿值,然后还有一个护士类,用来监听孩子类的这两个属性值的
Chidren.h
-
-
-
-
-
-
-
-
- #import <Foundation/Foundation.h>
-
- @interface Children : NSObject
-
- @property NSInteger *hapyValue;
- @property NSInteger *hurryValue;
-
-
- @end
Children.m
-
-
-
-
-
-
-
-
- #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{
-
-
- int value = _hapyValue;
- [self setHapyValue:--value];
-
- int values = _hurryValue;
- [self setHurryValue:--values];
- }
-
-
- @end
在初始化方法中,我们启动一个定时器,然后隔1s就去修改孩子类的值
Nure.h
-
-
-
-
-
-
-
-
- #import <Foundation/Foundation.h>
-
- @class Children;
- @interface Nure : NSObject{
- Children *_children;
- }
-
- - (id) initWithChildren:(Children *)children;
-
- @end
定义一个孩子属性
Nure.m
-
-
-
-
-
-
-
-
- #import "Nure.h"
- #import "Children.h"
-
- @implementation Nure
-
- - (id) initWithChildren:(Children *)children{
- self = [super init];
- if(self != nil){
- _children = children;
-
-
-
- [_children addObserver:self forKeyPath:@"hapyValue" options:NSKeyValueObservingOptionNew |NSKeyValueObservingOptionOld context:@"context"];
-
-
- [_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);
-
-
-
- if([keyPath isEqualToString:@"hapyValue"]){
-
-
-
- NSNumber *hapyValue = [change objectForKey:@"new"];
-
- NSInteger *value = [hapyValue integerValue];
-
- if(value < 90){
-
- }
- }else if([keyPath isEqualToString:@"hurryValue"]){
-
-
-
- NSNumber *hurryValue = [change objectForKey:@"new"];
-
- NSInteger *value = [hurryValue integerValue];
-
- if(value < 90){
-
- }
- }
-
- NSLog(@"%@",context);
-
-
-
-
- }
-
- - (void)dealloc{
-
-
- [_children removeObserver:self forKeyPath:@"hapyValue"];
- [_children removeObserver:self forKeyPath:@"hurryValue"];
-
- }
-
- @end
看到了在这里就开始进行监听操作了
下面来具体看一下如何做到监听的
1、添加监听对象
我们使用addObserver方法给孩子添加监听对象
第一个参数:监听者,这里是Nure,所以可以直接传递self
第二个参数:监听对象的属性名
第三个参数:监听这个属性的状态:这里可以使用|进行多种组合操作,属性的新值和旧值
第四个参数:传递内容给监听方法
-
-
- [_children addObserver:self forKeyPath:@"hapyValue" options:NSKeyValueObservingOptionNew |NSKeyValueObservingOptionOld context:@"context"];
-
-
- [_children addObserver:self forKeyPath:@"hurryValue" options:NSKeyValueObservingOptionNew |NSKeyValueObservingOptionOld context:@"context"];
2、监听方法
-
- - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(voidvoid *)context{
- NSLog(@"%@",change);
-
-
-
- if([keyPath isEqualToString:@"hapyValue"]){
-
-
-
- NSNumber *hapyValue = [change objectForKey:@"new"];
-
- NSInteger *value = [hapyValue integerValue];
-
- if(value < 90){
-
- }
- }else if([keyPath isEqualToString:@"hurryValue"]){
-
-
-
- NSNumber *hurryValue = [change objectForKey:@"new"];
-
- NSInteger *value = [hurryValue integerValue];
-
- if(value < 90){
-
- }
- }
-
- NSLog(@"%@",context);
-
-
-
-
- }
我们上面传递的第一个参数是监听者,这个方法也是在监听者中实现的,当属性值发生变化的时候,这个方法会被回调
这个方法的参数:
第一个参数:键值路径
第二个参数:监听对象
第三个参数:变化的值
第四个参数:传递的内容
我们看到代码中有一个特殊的参数:第三个参数:NSDirctionary类型的
其实我们如果不知道是干什么的,我们可以打印一下他的结果看一下,很简单,这里就不截图说明了
我们会发现他有两个键值对
key是:new和old
他们就是分别代表这个属性值变化的前后值,同时他们的得到也和之前我们添加监听对象时设置的第三个参数有关:
NSKeyValueObservingOptionNew |NSKeyValueObservingOptionOld
那个地方设置了几种状态,这里的NSDirctionary中就会有几个键值对
3、销毁方法
这个并不属于KVO的内容了,只是在这里用到了就顺便说一下
- - (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(方法名)
通知用完要移除