• 观察者模式的具体应用——通知(notification)机制和KVO(KeyValueObserving)机制


            在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

  • 相关阅读:
    利用正则表达式限制网页表单里的文本框输入内容小结
    实现注册页面中的倒计时功能代码
    asp.net中Response.Write用法小结
    数据库连接字符串
    asp.net中页面延时跳转代码
    C#网络编程socket使用总结
    CSS选择器总结
    C#面向对象三大特性总结
    HTML总结
    ASP.NET页面生命周期
  • 原文地址:https://www.cnblogs.com/arthas/p/4659427.html
Copyright © 2020-2023  润新知