iOS 中的观察者模式, KVO, 通知

一, KVO

Key-Value Observing,它提供一种机制,当指定的对象的属性被修改后,则对象就会接受到通知。每次指定的被观察的对象的属性被修改后,KVO自动通知相应的观察者。


KVO提供了一种key-value-observing的机制,也就是说可以通过监听key,来获得value的变化。用来在对象之间监听状态变化。使用KVO的类要遵循 协议,事实上,任何继承自NSObject的类,都遵循了这个协议。而Object C中,几乎所有的类都源自NSObject

什么情况下用KVO ?
1) 通常适用于根据A类(数据类)的某个属性值变化,B(view类)中的某个属性( 控件 状态)做出相应变化。体现了MVC的思想
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];
}

当然,也可以在需要的时候取消注册通知。

posted @ 2015-12-20 23:10  薛胖子  阅读(8)  评论(0编辑  收藏  举报  来源