iOS 趣谈设计模式——通知

【前言介绍】

iOS的一种设计模式,观察者Observer模式(也叫发布/订阅,即Publich/Subscribe模式)。

观察者模式,包含了通知机制(notification)和KVO(Key-value-observing)机制。

在这本文中,我们将介绍在日常项目当中经常使用到的通知机制这一种设计模式。

通知机制

委托机制是代理“一对一”的对象之间的通信,而通知机制是广播“一对多”的对象之间的通信;

一、是什么?【生活问题例子】

“短信天气预报”

当A类发送一条信息给通知中心时,注册为用户(观察者)的B类群就会收到相应的通知,并作出反应。

二、有什么用?【代码中的应用】

在不同类之间如何传递数据?

有几种方法:属性传递、代理协议,另外就是通知。

通知:在A类中创建的方法,B类中执行,且可以使用该通知携带数据传递给对方;

三、有什么不同?【与其他“通知”的不同?】

经常提到的通知,有“广播通知”、“本地通知”、“推送通知”

本文所介绍的就是广播通知,是实现观察者模式的一种机制,可以在一个应用中的多个对象之间进行通信传递数据。

而本地通知和推送通知主要是给用户发送“通知提示”,例如警告提示、声音、震动以及如图标上的红色数字提示。

第一种由“本地发送通知”给用户,第二种由第三方应用发送给苹果官方的远程服务器,然后再由服务器“推送通知”给用户。

四、产品经理:老规矩,代码拿来~【具体实现】

过程:

  • 在通知机制中,需要(或者说感兴趣)接收某个通知的信息的所有对象都可以成为接收者,首先注册成为观察者。
  • 进行注册后,通知中心就会把发布者发送的通知信息,广播给注册过该通知的观察者。且观察者只能接收到通知中心的信息,不能知道通知是谁投送的。
  • 最后,接受者不想再对关注该通知的信息时,可以给通知中心发生解除注册的信息,之后都不再接收到通知了。

1.获取通知中心(NSNotificationCenter)对象:(就像获取移动营运商短信中心的权限,作为媒介)

发布、注册、解除通知都需要使用通知中心,负责协助不同对象、不同类之间的消息通信。

[NSNotificationCenter  defaultCenter];   //需要注意的是,通知中心也是一个单例

2.发布(A类)和接收(B类)

a.做为发布者的A类发送通知:

可以使用一下三个方法:

- (void)postNotification:(NSNotification *)notification;

- (void)postNotificationName:(NSString *)aName object:(id)anObject;

- (void)postNotificationName:(NSString *)aName object:(id)anObject userInfo:(NSDictionary *)aUserInfo;
  • postNotificationName:指定消息名称;
  • object:指定发消息者;
  • userInfo:通知中用于传递参数的载体,传递的方法是把参数放在NSDictionary类型的userInfo中。例如:NSDictionary *dict = [notification userInfo];

一般使用第三个方法,如果参数不需要的,可以设置为nil.

b.注册通知,加入观察者:

做为观察者B类注册通知,进行监听:

- (void)addObserver:(id)observer selector:(SEL)aSelector name:(nullable NSString *)aName object:(nullable id)anObject;
 //@selector中为回调方法,在本类中对通知进行相应的处理,name为通知名称、object为对象;

剖析:

  • object == nil,那么客户对象(self)将收到任何对象发出NSWindowDidBecomeMainNotification的通知消息;
  • name == nil,那么观察者将接收到object对象的所有消息,但是确定不了接收这些消息的顺序。
  • object == nil,name == nil,那么该观察者将收到所有对象的所有消息。

对于一个任意的观察者observer,如果不能保证其对应的selector有本类自定义的方法(例如:MyMethod),可采用[observer respondsToSelector:@selector(MyMethod:)]] 进行检查。

所以完整的添加观察者过程为:

if([observer respondsToSelector:@selector(MyMethod:)]) {
        [[NSNotificationCenter defaultCenter] addObserver:observer selector:@selector(MyMethod:) name:NSWindowDidBecomeMainNotification object:nil];
    }  

当然在苹果API中也有另外一个注册观察者的方法:

- (id <NSObject>)addObserverForName:(nullable NSString *)name object:(nullable id)obj queue:(nullable NSOperationQueue *)queue usingBlock:(void (^)(NSNotification *note))block

此方法是支持在该方法中进行block回调的,而queue参数就是表示此模块在queue队列中进行。

但是这方法一般不采用,所以建议使用第一种方法进行观察者的创建。

c.移除通知

由于通知中心不会retain观察者对象,因此注册过的对象必须在释放之前注销掉,如果不这样的话,当该通知再次出现时,通知中心会向已释放的观察者对象发送消息,从而导致应用崩溃。

在ARC下,系统会自动回收无用的通知对象内存,但是由于系统回收机制ARC有一定的延迟性,所以即使不会出错,也建议养成习惯,对通知进行手动释放无用的通知。

移除有2种方法:

//释放所有通知- (void)removeObserver:(id)observer;//释放名称为aName的通知
- (void)removeObserver:(id)observer name:(nullable NSString *)aName object:(nullable id)anObject;

一般在视图控制器中,可以在“didReceiveMemoryWarning:”中发送解除消息:

-(void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
    //移除观察者
    [[NSNotificationCenter defaultCenter]removeObserver:self];
}

五、那些年我们用过的系统通知名称~

系统自带的也有许多有用的通知,我们只需要注册为相应的通知接收对象,就能根据通知状态的变化发生相应的数据改变。

部分系统通知名称如下:

UIApplicationDidFinishLaunchingNotification // 应用程序启动后
UIApplicationDidBecomActiveNotification     //进入前台
UIApplicationWillResignActiveNotification   //应用将要进入后台UIApplicationDidEnterBackgroundNotification //进入后台
UIKeyboardWillShowNotification        // 键盘即将显示
UIKeyboardDidShowNotification         // 键盘显示完毕
UIKeyboardWillHideNotification        // 键盘即将隐藏
UIKeyboardDidHideNotification         // 键盘隐藏完毕 

六、举个栗子:“??”

本文有2个例子:

  • 一个是完整的通知发布、接收、解除过程;
  • 一个是系统通知名称的应用(以第一个UIApplicationWillResignActiveNotification
    为例);

(by:觉得文章太长不想看这段的童鞋,也可以到github上下载啊左的demo,自己琢磨琢磨:Mydemo1、 Mydemo2

点击“DownLoad ZIP”按钮就可以了。一般使用Safari浏览器下载得了,啊左用QQ浏览器貌似下载不了...囧)

【本次开发环境: Xcode:7.2     iOS Simulator:iphone6   By:啊左】

1.完整的通知发布、接收、解除过程:

UI控件摆放如下,视图、控件的背景可以自己设置成比较明显的颜色,便于观察:

A视图创建一个textView用于显示B视图传递过来的信息,一个按钮用于切换到B视图;

B视图创建一个文本框用于更新信息,一个按钮用于把文本框的信息更新并返回到视图A。

然后,点击A类的按钮,并且按住control拖拽到B视图的控制器后松开鼠标,在弹出的选择框(如下图)选择:“Present Modally”用于创建A、B控制器之间的模态类型的Segue。

接下来,我们需要在新建一个视图控制器B类SeocndViewController:

回到故事板中,选择B视图控制器,打开标识检查器(下图第一排第三个选项),选择class为:SeocndViewController。这就使代码与故事板中的视图控制器对应起来。(A视图默认对应ViewController,如果有错误,可以检查一下。)

然后我们打开辅助编辑器,按住control,拖拽A视图中的文本连接到对应的输出口,这里我们命名为“myLabel”.

以此方式,继续为B类中的文本框连接到代码中,并命名为:“MyTextView”,

为B类的按钮添加行为,方法名为:“saveBtn:”,

啊左还是觉得上代码实在点:

(ViewController.h)

#import <UIKit/UIKit.h>

@interface ViewController : UIViewController

@property (weak, nonatomic) IBOutlet UITextView *myLabel;   //每次视图打开后,监听B类的数据是否发生变化,如有变化,在这个文本视图中显示更新。

@end

(ViewController.m)

#import "ViewController.h"

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    //1.注册为观察者,监听B视图中的通知
    [[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(AMethod:) name:@"MyNotificationName" object:nil];
}

-(void)AMethod:(NSNotification *)notification
{
    //2.获取通知携带的数据,更新label的文本信息
    NSDictionary *dictData = [notification userInfo];
    NSString *str = [dictData objectForKey:@"MyUserInfoKey"];
    self.myLabel.text = str;
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];

    //3.移除所有通知
    [[NSNotificationCenter defaultCenter]removeObserver:self];
}

@end

(SecondViewController.h)

#import <UIKit/UIKit.h>

@interface SecondViewController : UIViewController

@property (weak, nonatomic) IBOutlet UITextField *MyTextView;  //文本框,用于更新传递给ViewController视图的数据

- (IBAction)saveBtn:(UIButton *)sender;                        //保存返回按钮事件

@end

(SecondViewController.m)

#import "SecondViewController.h"

@interface SecondViewController ()

@end

@implementation SecondViewController

- (void)viewDidLoad {
    [super viewDidLoad];
}

- (IBAction)saveBtn:(UIButton *)sender {

    //返回视图A并发布通知
    [self dismissViewControllerAnimated:YES completion:^{
        //1.创建userInfo携带的信息
        NSString *str = self.MyTextView.text;
        NSDictionary *dictData = [NSDictionary dictionaryWithObject:str forKey:@"MyUserInfoKey"];
        //2.发布信息
        [[NSNotificationCenter defaultCenter]postNotificationName:@"MyNotificationName" object:nil userInfo:dictData];
    }];
}
@end

验证:

第一次A视图的文本视图中没有显示数据,点击按钮“确定切换页面”,打开视图B,在文本框中输入信息(例如123),点击“保存返回”按钮,在A视图的文本视图中看到更新的信息:123。

by:有需要的童鞋可以到github上下载啊左的demo:Mydemo1

2.一个是系统通知名称的应用(以UIApplicationWillResignActiveNotification为例):

UIApplicationWillResignActiveNotification的意思是应用即将进入后台的这个时刻。

首先,创建UI界面如下,相比第一个例子,这个会简单很多:一个按钮+一个显示颜色的UIView视图。

创建一个命名为“myView”的UIView控件,一个方法为“changeColorBtn:”的按钮行为即可,关联ViewController控制器。

代码如下:

(ViewController.h)

#import <UIKit/UIKit.h>

@interface ViewController : UIViewController

@property (weak, nonatomic) IBOutlet UIView *myView;
- (IBAction)changeColorBtn:(UIButton *)sender;

@end

(ViewController.m)

#import "ViewController.h"

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    //1. 注册为观察者
    [[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(applicationWillResignActiveNotification:) name:UIApplicationWillResignActiveNotification object:nil];
}
//2.当应用即将进入后台时,调用通知回调方法:
-(void)applicationWillResignActiveNotification:(NSNotification *)notification
{
    //返回后台的过程,把视图背景改为红色;
    self.myView.backgroundColor = [UIColor redColor];
}

- (IBAction)changeColorBtn:(UIButton *)sender {

    //按钮把视图背景改为黄色;
    self.myView.backgroundColor = [UIColor yellowColor];
}
@end

视图第一次打开,视图为默认白色:

点击按钮,视图变为黄色:

按住“command+shift”,双击H,进入iOS多任务栏;

或者按住“command+shift”,单击H,回到模拟器主界面。

都可以看到视图变为红色。

且回到应用后,颜色仍然是红色。

表示,当应用从活跃的状态进入非活跃状态的时候,系统自动发送“UIApplicationWillResignActiveNotification”这个通知,如有注册监听者(观察者),则执行回调方法。

by:有需要的童鞋可以到github上下载啊左的demo:Mydemo2

时间: 2024-10-08 09:20:14

iOS 趣谈设计模式——通知的相关文章

iOS 浅谈本地通知 UILocalNotification

1.创建本地通知 UILocalNotification *local = [[UILocalNotification alloc] init]; 2.设置通知显示的时间 local.fireDate = [NSDate date]; 3.设置默认时区 local.timeZone = [NSTimeZone defaultTimeZone]; 4.设置提示内容 local.alertBody = @JPG下载完成,请即时查看!; 5.这个通知到时间时,你的应用程序右上角显示的数字. local

趣谈 32 种设计模式

32种设计模式趣谈 好东西不得不转 在网上看见了这篇文章,作者以轻松的语言比喻了java的32种模式,有很好的启发作用. 创建型模式 1.FACTORY-追MM少不了请吃饭了,麦当劳的鸡翅和肯德基的鸡翅都是MM爱吃的东西,虽然口味有所不同,但不管你带MM去麦当劳或肯德基,只管向服务员说"来四个鸡翅"就行了.麦当劳和肯德基就是生产鸡翅的Factory 工厂模式:客户类和工厂类分开.消费者任何时候需要某种产品,只需向工厂请求即可.消费者无须修改就可以接纳新产品.缺点是当产品修改时,工厂类也

IOS开发常用设计模式

IOS开发常用设计模式 说起设计模式,感觉自己把握不了笔头,所以单拿出iOS开发中的几种常用设计模式谈一下. 单例模式(Singleton) 概念:整个应用或系统只能有该类的一个实例 在iOS开发我们经常碰到只需要某类一个实例的情况,最常见的莫过于对硬件参数的访问类,比如UIAccelerometer.这个类可以帮助我们获得硬件在各个方向轴上的加速度,但是我们仅仅需要它的一个实例就够了,再多,只会浪费内存. 所以苹果提供了一个UIAccelerometer的实例化方法+sharedAcceler

闲话iOS的MVC设计模式

模式是经验知识的复制应用.MVC设计模式在不同的开发平台有不同阐述和应用.目前在网路上可以搜索出java版本.c++版本.c#版本的,也有ios版本的.我这里也发布这篇关于MVC设计模式的文章,用我的缘走你的路. 写在前面的话 若然不用设计模式,难道就不能开发设计程序了吗?不然.那么设计模式给我们带来什么呢?如果你不学习别人总结出来的设计模式,就能轻松.快捷.真正地解决问题,而且还乐意再来一次,我相信你不需要别人的设计模式了.如果??某些问题,让你很挠头,让你不敢再回首,不妨借助别人总结出来的设

iOS开发与设计模式 - MVC

iOS开发与设计模式 - MVC 最近在学习GoF的设计模式这本书,粗略的浏览了一遍,真是好书.好书就应该好好读,因此很有必要从实际的言语.项目理解设计模式. 我是做iOS开发的,自然就从这方面入手(脑). MVC iOS开发最基本的一个模式就是MVC, M指model,V指view,C指controller,有很多文章对它们是什么,它们的关系,它们如何交互进行了详细的说明,本文就不再展开说明了,仅放一张图供大家参考(来自斯坦福大学ios课程)  ViewController 是什么? 在iOS

从变化逻辑的封装谈设计模式

通常来说,对于某个满足了我们大部分需要的类,可以创建一个它的子类,并只改变其中我们不期望的部分(需要变化部分).只是继承一个类,就可以重用该类的代码,这是一件多美好的事情啊!不过,像大多数美好的事情一样,过度使用往往会变得不美好.根据可替换原则(LSP), public 继承具有概念上的现实意义,它代表的是一种is-a关系.使用继承之前一定要问问是否真的属于is-a的关系,否则继承非常容易被过度使用.基于此,建议优选使用对象组合(object composition)而不是类继承(class i

浅谈设计模式的学习(下)

时间过得真快啊,不知不觉又要周末了,借这个周末时间.把<浅谈设计模式的学习(下)>补上吧. 在<浅谈设计模式的学习(中)>中,说到了保持抽象的思维.接下来说一下第四点,做一个分享,也记录一下自己的学习历程. 4.学习设计模式,就不要把它看的太认真    设计模式是一个编程思想,它不是具体的代码套路.举个例子说明一下: 由于家传,接触到了一些中国的传统武术.当我与那些不懂传统武术的人交流的时候,他们总是认为中国的传统武术都是些套路.花架子,只是用来好看.在他们认为,两人打架,别人出拳

浅谈设计模式的学习(中)

在<浅谈设计模式的学习(上)>中我说到了设计模式的基石-----抽象思维.为什么需要抽象思维呢?因为越抽象就越不容易出错,就像有些领导人说话:坚持改革开放.但怎么算坚持改革开放呢,没有具体的标准,因事而异,所以就不容易违背这个坚持改革开放的原则了. 3.学习设计模式,要保持抽象的思维     什么是抽象思维呢?真的不好说,抽象的东西往往难以说明白,听了也难以搞明白,还是通过具体的例子来说吧 有这么一个学生请假的场景,如果请假时间一天以内则有班长批准就可以了,三天以内则需要老师批准,超过三天就得

趣谈并发2:认识并发编程的利与弊

读完本文你将了解: 多线程的优点 1提高资源利用率 2响应更快 多线程的缺点 1增加资源消耗 2上下文切换的开销 3设计编码测试的复杂度增加 Java 内存模型与 CPU 内存简介 Java 中的堆 Java 中的栈 计算机中的内存寄存器缓存 多线程可能出现的问题 竞态条件与临界区 内存可见性 总结 Thanks 从上篇文章 趣谈并发(1):全面认识 Thread 我们了解了 Java 中线程的基本概念和关键方法. 在开始使用线程之前,我觉得我们有必要先了解下多线程给我们带来的好处与可能造成的损