您的位置:首页 > 产品设计 > UI/UE

iOS---KVO(Key Value Observing) 观察者模式之解析与应用

2016-08-26 18:17 447 查看
一、概述

KVO(Key Value Observing) 观察者设计模式。通过KVO这种机制对象可以通过它得到其他对象的某个属性的变更通知。KVO可以让视图对象经过控制器观察模型对象的变更从而做出更新等操作。

KVO提供一种机制,指定一个被观察对象(例如StockData类),当对象某个属性(如StockData中的变量 price)发生更改时,对象会获得通知,并作出相应处理;【且不需要给被观察的对象添加任何额外代码,就能使用KVO机制】

在MVC设计模式下,KVO机制很适合实现model模型和view视图之间的通讯。

二、使用方法

1、注册观察者,实施监听。

[_stockData addObserver:self forKeyPath:@"price" options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld context:@"我观察的是name的属性"]; // 注册观察者

// 参数说明:
// object: 被观察的对象 _stockData
// observer: 观察对象 self
// forKeyPath: 被监测的那个对象的属性所在的路劲 如"price"
// optons: 有4个值,分别是:
// NSKeyValueObservingOptionNew 把更改之前的值提供给处理方法
// NSKeyValueObservingOptionOld 把更改之后的值提供给处理方法
// NSKeyValueOpservingOptionInitial 把初始化的值提供给处理方法,一旦注册,立马就会调用一次。通常它会带有新值,而不会带有旧值。
// NSKerValueOpservingOptionPrior: 分2次调用。在值改变之前和值改变之后。
// context:可以带入一些参数,任何类型都可以。强制转就可以。


2、在回调方法中处理属性发生的变化

//实现回调方法
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context{
NSLog(@"被监测的那个对象的属性所在的路劲%@", keyPath);
NSLog(@"被观察者:%@", object);
NSLog(@"属性所在状态下的值:%@", change);
NSLog(@"在注册观察者的时候,传过来的context:%@", context);

if ([keyPath isEqualToString:@"price"]) {
self.myLabel.text = [[_stockData valueForKey:@"price"] stringValue];
}
}

// 参数说明
// keyPath对应forKeyPath 被监测的那个对象的属性所在的路劲
// object:被观察者的对象
// change:对应options里的NSKeyValueObservingOptionNew/Old/Initial/Prior 属性所在状态下的值
// context:在注册观察者的时候,传过来的context


3、移除观察者

- (void)dealloc
{
[self.stockData removeObserver:self forKeyPath:@"price"];
//    [super dealloc]; //开启ARC 此处不需要调用
NSLog(@"移除观察者");
// 移除观察者

}


三、代码实例

// 1、修改值



self.stockData = [[StockData alloc] init];
[_stockData setValue:@"searph" forKey:@"stockName"]; // 这边使用KVC修改值
[_stockData setValue:[NSNumber numberWithFloat:10.0] forKey:@"price"]; // 这边使用KVC修改值
[_stockData addObserver:self forKeyPath:@"price" options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld context:@"我观察的是name的属性"]; // 注册观察者

self.myLabel = [[UILabel alloc]initWithFrame:CGRectMake(100, 70, 100, 30 )];
_myLabel.textColor = [UIColor redColor];
_myLabel.text = [[_stockData valueForKey:@"price"] stringValue];
[self.view addSubview:_myLabel];

UIButton * c = [UIButton buttonWithType:UIButtonTypeCustom];
c.frame = CGRectMake(50, 150, 200, 30);
[c setTitle:@"改变Label值" forState:UIControlStateNormal];
c.backgroundColor = [UIColor blueColor];
[c addTarget:self action:@selector(buttonAction) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:c];


/**
* 点击按钮修改对象值
*/
- (void)buttonAction{
// 修改属性值
// _stockData.price = 20.0;
self.count++;
[_stockData setValue:[NSNumber numberWithFloat:10.0 + self.count] forKey:@"price"];

}

//实现回调方法
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context{
NSLog(@"被监测的那个对象的属性所在的路劲%@", keyPath);
NSLog(@"被观察者:%@", object);
NSLog(@"属性所在状态下的值:%@", change);
NSLog(@"在注册观察者的时候,传过来的context:%@", context);

if ([keyPath isEqualToString:@"price"]) {
self.myLabel.text = [[_stockData valueForKey:@"price"] stringValue];
}

// 打印结果
/**
2016-08-26 14:39:09.093 YJKVODemo[7342:816462] 被监测的那个对象的属性所在的路劲price
2016-08-26 14:39:09.093 YJKVODemo[7342:816462] 被观察者:<StockData: 0x7ff5fb701380>
2016-08-26 14:39:09.093 YJKVODemo[7342:816462] 属性所在状态下的值:{
kind = 1;
new = 20;
old = 10;
}
2016-08-26 14:39:09.093 YJKVODemo[7342:816462] 在注册观察者的时候,传过来的context:我观察的是name的属性
*/
}

// dealloc移除观察者
- (void)dealloc { [self.stockData removeObserver:self forKeyPath:@"price"]; // [super dealloc]; //开启ARC 此处不需要调用 NSLog(@"移除观察者"); // 移除观察者 }


// 2、导航栏颜色渐变 TestViewController



- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor whiteColor];

// 注册观察者
[self.tableView addObserver:self forKeyPath:@"contentOffset" options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld context:nil];
self.navigationController.navigationBar.backgroundColor = [UIColor redColor];

}

/**
*  监听属性值发送改变时回调
*
*  @param keyPath 被观察者的属性路劲
*  @param object  监听者
*  @param change  改变的值
*  @param context 传过来的值
*/
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context
{
NSLog(@"被监测的那个对象的属性所在的路劲%@", keyPath);
NSLog(@"被观察者:%@", object);
NSLog(@"属性所在状态下的值:%@", change);
NSLog(@"在注册观察者的时候,传过来的context:%@", context);

CGFloat offset = self.tableView.contentOffset.y;
CGFloat delta = offset / 64.f + 1.f;

delta = MAX(0, delta);
self.navigationController.navigationBar.alpha = MIN(1, delta);

NSLog(@"offset = %f--delta = %f", offset, delta);
}
- (void)dealloc
{
// 移除 观察者
[self.tableView removeObserver:self forKeyPath:@"contentOffset"];
}


// 3、RGB 颜色变化 ChangeColorViewController



//
//  ChangeColorViewController.m
//  YJKVODemo
//
//  Created by GongHui_YJ on 16/8/26.
//  Copyright © 2016年 YangJian. All rights reserved.
//

#import "ChangeColorViewController.h"

@interface ChangeColorViewController ()

@property (weak, nonatomic) IBOutlet UISlider *RSider;
@property (weak, nonatomic) IBOutlet UISlider *GSider;
@property (weak, nonatomic) IBOutlet UISlider *BSider;

@property (weak, nonatomic) IBOutlet UILabel *RLabel;
@property (weak, nonatomic) IBOutlet UILabel *GLabel;
@property (weak, nonatomic) IBOutlet UILabel *BLabel;

@property (weak, nonatomic) IBOutlet UILabel *backgroundChangeLabel;

@property (strong, nonatomic) UIColor *changeColor;

@end

@implementation ChangeColorViewController

- (void)viewDidLoad {
[super viewDidLoad];
[self.RSider addTarget:self action:@selector(rsiderAtion:) forControlEvents:UIControlEventValueChanged];
[self.GSider addTarget:self action:@selector(gsiderAction:) forControlEvents:UIControlEventValueChanged];
[self.BSider addTarget:self action:@selector(bsiderAction:) forControlEvents:UIControlEventValueChanged];

// 注册观察者
[self addObserver:self forKeyPath:@"changeColor" options:NSKeyValueObservingOptionInitial context:nil];
}

- (void)rsiderAtion:(UISlider *)sider{
self.changeColor = [UIColor colorWithRed:sider.value green:self.GSider.value blue:self.BSider.value alpha:1];
self.RLabel.text = [NSString stringWithFormat:@"%f", sider.value];
}

- (void)gsiderAction:(UISlider *)sider{
self.changeColor = [UIColor colorWithRed:self.RSider.value green:sider.value blue:self.BSider.value alpha:1];
self.GLabel.text = [NSString stringWithFormat:@"%f", sider.value];
}

- (void)bsiderAction:(UISlider *)sider{
self.changeColor = [UIColor colorWithRed:self.RSider.value green:self.GSider.value blue:sider.value alpha:1];
self.BLabel.text = [NSString stringWithFormat:@"%f", sider.value];
}

/**
*  回调改变
*
*  @param keyPath <#keyPath description#>
*  @param object  <#object description#>
*  @param change  <#change description#>
*  @param context <#context description#>
*/
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context
{
if ([keyPath isEqualToString:@"changeColor"]) {
self.backgroundChangeLabel.backgroundColor = self.changeColor;
} else {
[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
}
}

- (void)dealloc
{
// 移除观察者
[self removeObserver:self forKeyPath:@"changeColor"];
}

- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}

/*
#pragma mark - Navigation

// In a storyboard-based application, you will often want to do a little preparation before navigation
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
// Get the new view controller using [segue destinationViewController].
// Pass the selected object to the new view controller.
}
*/

@end


四、小结

今天对KVO进一步的深入理解,但在实际开发中用的也不多,一般为model层对controller和view进行的改变值。用起来还是蛮简单,只要三步。

Demo地址:http://download.csdn.net/detail/yj229201093/9614098

五、引用

http://www.androiddev.net/kvo/

http://www.cnblogs.com/azuo/p/5442319.html

感谢大神们的博客一路相助···
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息