iOS 中的观察者模式, KVO, 通知
一, KVO
Key-Value Observing,它提供一种机制,当指定的对象的属性被修改后,则对象就会接受到通知。每次指定的被观察的对象的属性被修改后,KVO自动通知相应的观察者。
KVO提供了一种key-value-observing的机制,也就是说可以通过监听key,来获得value的变化。用来在对象之间监听状态变化。使用KVO的类要遵循 协议,事实上,任何继承自NSObject的类,都遵循了这个协议。而Object C中,几乎所有的类都源自NSObject
什么情况下用KVO ?
2) 比如用于用户界面交互,当多个View共同使用了同一个实体(数据类),当这个实体中的某个属性改变时,如果需要更新多个界面,KVO就能发挥作用了
3) 拷贝大文件的时候监听已拷贝的文件大小,该功能适合用于loading的时候用
model中的定义:
@interface StockData : NSObject { NSString * stockName; float price; } @end @implementation StockData @end
controller中使用
- (void)viewDidLoad { [super viewDidLoad]; stockForKVO = [[StockData alloc] init];
//使用KVC进行赋值
//注意: 想要使用KVO得到新的变化后的数据, Model的属性必须要求使用KVC进行赋值才行. [stockForKVO setValue:@"searph" forKey:@"stockName"]; [stockForKVO setValue:@"10.0" forKey:@"price"];
//使用KVO, 注册观察者 [stockForKVO addObserver:self forKeyPath:@"price" options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld context:NULL];
/*注册通知
observer:观察者,也就是KVO通知的订阅者。订阅着必须实现 observeValueForKeyPath:ofObject:change:context:方法 keyPath:描述将要观察的属性,相对于被观察者。 options:KVO的一些属性配置;有四个选项。 context: 上下文,这个会传递到订阅着的函数中,用来区分消息,所以应当是不同的。options所包括的内容
NSKeyValueObservingOptionNew:change字典包括改变后的值 NSKeyValueObservingOptionOld:change字典包括改变前的值 NSKeyValueObservingOptionInitial:注册后立刻触发KVO通知 NSKeyValueObservingOptionPrior:值改变前是否也要通知(这个key决定了是否在改变前改变后通知两次)*/
//创建Label目的是为了显示文字 myLabel = [[UILabel alloc]initWithFrame:CGRectMake(100, 100, 100, 30 )]; myLabel.textColor = [UIColor redColor]; myLabel.text = [stockForKVO valueForKey:@"price"]; [self.view addSubview:myLabel];
//创建Button, 在点击事件中改变Model的属性值 UIButton * b = [UIButton buttonWithType:UIButtonTypeRoundedRect]; b.frame = CGRectMake(0, 0, 100, 30); [b addTarget:self action:@selector(buttonAction) forControlEvents:UIControlEventTouchUpInside]; [self.view addSubview:b]; }
用户单击View中的button调用控制器中的action去更改模型中的数据
-(void) buttonAction { [stockForKVO setValue:@"20.0" forKey:@"price"]; }
当被观察对象的值发生变化的时候, 就会立即出发该方法
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
/*keyPath:被监听的keyPath , 用来区分不同的KVO监听。 object: 被观察修改后的对象(可以通过object获得修改后的值) change:保存信息改变的字典(可能有旧的值,新的值等) context:上下文,用来区分不同的KVO监听。*/
if([keyPath isEqualToString:@"price"]) { myLabel.text = [stockForKVO valueForKey:@"price"]; } }
视图dealloc需要取消观察
- (void)dealloc
{
[super dealloc];
//一定不要忘记移除观察者 [stockForKVO removeObserver:self forKeyPath:@"price"]; [stockForKVO release]; }
KVO需要注意的两点:
一, KVO和Context:
由于Context通常用来区分不同的KVO,所以context的唯一性很重要。通常,我的使用方式是通过在当前.m文件里用静态变量定义。
二, KVO与线程:
KVO的响应和KVO观察的值变化是在一个线程上的,所以,大多数时候,不要把KVO与多线程混合起来。除非能够保证所有的观察者都能线程安全的处理KVO
二, 通知
通知使用起来非常的简单:
首先定义回调,即发生通知了我应该做啥事。
- (void)callBack{ NSLog(@"收到通知我该干点啥呢?"); }
其次,注册通知,即告诉通知中心,我对啥通知感兴趣
[[NSNotificationCenter defaultCenter] addObserver: self
selector: @selector(callBack)
name: @"A类通知"
object: nil];
第三,在程序任何一个地方都可以发送通知
- (void)getNotofocation{ NSLog(@"get it."); //发出通知 [[NSNotificationCenter defaultCenter] postNotificationName:@"A类通知" object:self]; }
当然,也可以在需要的时候取消注册通知。