观察者模式的具体应用——通知(notification)机制和KVO(Key-Value-Observing)机制

        在Cocoa Touch框架中,观察者模式的具体应用有两个——通知(notification)机制和KVO(Key-Value-Observing)机制。

        

        1、通知机制。

        在iOS中通知主要有以下三种:广播通知、本地通知和推送通知。本文所说的通知是广播通知,广播通知除了名字和后两者相似之外其他完全不同。广播通知是Cocoa Touch框架中实现观察者模式的一种机制,它可以在一个应用内部的多个对象之间发送消息;本地通知和推送通知是给用户一种提示,比如警告对话框、发出声音、震动或者在应用图标上面显示数字等。本地通知由本地iOS发出,推送通知由第三方程序发送给苹果的远程服务器,再由远程服务器推送给iOS的制定应用。

        通知机制有一个非常重要的类——NSNotificationCenter(通知中心),这是一个单例类。在通知机制中,对某个通知感兴趣的的对象都可以向通知中心发出addObserver:selector:name:object:消息注册为接受者,在投送对象投送给通知中心时,通知中心就会把通知广播给注册过的接收者,投送对象与接收者是一对多的关系。接收者可以给通知中心发送removeObserver:name:object消息解除注册,以后不再接收通知。

        下面的代码是测试接收自定义通知和接收系统通知:

 

- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    
    //将当前的controller注册为通知接收者
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(receiveNotification:) name:@"TestNotification" object:nil];
    
    //添加一个发送通知的按钮
    UIButton *button = [UIButton buttonWithType:UIButtonTypeSystem];
    [button setTitle:@"Send" forState:UIControlStateNormal];
    button.frame = CGRectMake(50, 300, 220, 25);
    [button addTarget:self action:@selector(sendNotification) forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview:button];
    
    //监听系统通知
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleEnterBackground:) name:UIApplicationDidEnterBackgroundNotification object:nil];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleEnterForeground:) name:UIApplicationWillEnterForegroundNotification object:nil];
}

- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
    //解除通知
    [[NSNotificationCenter defaultCenter] removeObserver:self];
}

/**
 *  发送通知
 */
- (void)sendNotification {
    NSDictionary *dic = [NSDictionary dictionaryWithObject:@"Test" forKey:@"key"];
    [[NSNotificationCenter defaultCenter] postNotificationName:@"TestNotification" object:nil userInfo:dic];
}


/**
 *  接收自定义通知
 *
 *  @param notification 接收到的通知
 */
- (void)receiveNotification:(NSNotification *)notification {
    NSDictionary *dic = notification.userInfo;
    
    NSString *value = [dic objectForKey:@"key"];
    
    NSLog(@"Receive notification:%@", value);
}

/**
 *  接收应用进入后台的通知
 *
 *  @param notification 接收到的通知
 */
- (void)handleEnterBackground:(NSNotification *)notification {
    NSLog(@"应用进入到后台。");
}

/**
 *  接收应用进入前台的通知
 *
 *  @param notification 接收到的通知
 */
- (void)handleEnterForeground:(NSNotification *)notification {
    NSLog(@"应用进入前台。");
}

        NSNotificationCenter是一个单例类,defaultCenter方法创建并获得一个实例。NSNotificationCenter的addObserver:selector:name:object方法用来注册通知,selector指定的方法是接收并处理通知的方法,name是通知的名字,object是投送通知时传递过来的对象,userInfo是投送通知时传递的字典对象,可以用它来传递一些数据。

        postNotificationName:object:userInfo方法是用来发送通知的,第一个参数是通知的名字,object是发送通知时传递的一个对象,如果接收者不需要,可以将其设为nil。

 

UIApplicationDidEnterBackgroundNotification
UIApplicationWillEnterForegroundNotification

        这两个是系统提供的通知的名字,用来接收系统发送的一些通知。除了应用生命周期的不同阶段有不同的通知外,很多控件也会在某些事件发生时投送通知,例如UITextField控件,在编辑过程的不同阶段,UITextField控件会分别发送如下通知:UITextFieldTextDidBeginEditingNotification、UITextFieldTextDidChangeNotification和UITextFieldTextDidEndEditingNotification。

 

        2、KVO机制

        KVO不像通知机制那样通过一个通知中心通知所有通知接收者,而是在对象属性变化时通知会被直接发送给观察者对象。

        在KVO机制中,属性发生变化的对象需要发出消息addObserver:forKeyPath:options:context给注册观察者,使得观察者关注它的某个属性的变化。观察者需要重写observerValueForKeyPath:ofObject:change:context方法来响应属性的变化。

        下面是KVO的一个demo的部分实现代码:

 

//
//  WBViewController.h
//  KVODemo
//
//  Created by 韩学鹏 on 15/6/26.
//  Copyright (c) 2015年 韩学鹏. All rights reserved.
//

#import <UIKit/UIKit.h>

@interface WBViewController : UIViewController
@property (nonatomic, strong)NSString *textString;
@end

//
//  WBViewController.m
//  KVODemo
//
//  Created by 韩学鹏 on 15/6/26.
//  Copyright (c) 2015年 韩学鹏. All rights reserved.
//

#import "WBViewController.h"
#import "WBTextObserver.h"
@interface WBViewController () {
    UITextField *_textField;
    WBTextObserver *_observer;
}

@end

@implementation WBViewController

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
    if (self) {
        // Custom initialization
    }
    return self;
}

- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    
    //输入框
    _textField = [[UITextField alloc] initWithFrame:CGRectMake(10, 200, 200, 25)];
    [_textField setBorderStyle:UITextBorderStyleRoundedRect];
    [self.view addSubview:_textField];
    
    //按钮,改变字符串
    UIButton *button = [UIButton buttonWithType:UIButtonTypeSystem];
    [button setTitle:@"Change" forState:UIControlStateNormal];
    button.frame = CGRectMake(200, 200, 80, 25);
    [button addTarget:self action:@selector(textChangeFunc) forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview:button];
    
    //观察者
    _observer = [[WBTextObserver alloc] init];
    [self addObserver:_observer forKeyPath:@"textString" options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld context:@"context"];
}

- (void)textChangeFunc {
    self.textString = _textField.text;
}

        观察者类:

//
//  WBTextObserver.h
//  KVODemo
//
//  Created by 韩学鹏 on 15/6/26.
//  Copyright (c) 2015年 韩学鹏. All rights reserved.
//

#import <Foundation/Foundation.h>

@interface WBTextObserver : NSObject

@end

//
//  WBTextObserver.m
//  KVODemo
//
//  Created by 韩学鹏 on 15/6/26.
//  Copyright (c) 2015年 韩学鹏. All rights reserved.
//

#import "WBTextObserver.h"

@implementation WBTextObserver

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
    NSLog(@"WBTextObserver %@---%@", keyPath, [change objectForKey:NSKeyValueChangeNewKey]);
}

@end

        textSting属性是要观察的属性,WBTextObserver是自定义的观察者类,它负责观察textString属性的变化,_textField是测试需要添加的一个输入框,用来改变textString的值。

 

[self addObserver:_observer forKeyPath:@"textString" options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld context:@"context"];

这一行代码是关键代码,它用来告诉观察者对象_observer开始观察属性textString的变化,self是被观察的对象,_observer是观察者对象,forKeyPath是设置被关注对象的属性,options是为属性变化设置的选项,本例中New和Old表示把属性的新旧两个值都传递给观察者。

    因为NSObject类实现了NSKeyValueObserving协议,所以WBTextObserver只需继承NSObject类。

observeValueForKeyPath:ofObject:change:context方法的observeValueForKeyPath参数是被关注的属性,ofObject是被关注的对象,change是字典类,包含了属性变化的内容,这些内容与注册时属性变化设置的选项(options参数)有关,context是注册时传递的上下文内容。

 

 

最后附上通知机制和KVO机制的测试工程:通知机制和KVO机制Demo

 

posted @ 2015-07-19 19:46  驴车手  阅读(332)  评论(0编辑  收藏  举报