iOS通知中心升级 -可设置按优先级执行block
简单介绍下,这是需求驱动中发现iOS的NotificationCenter有很多功能无法实现,于是对其进行了一层包装。相当于手动管理观察者栈和监听者期望执行的事件,因此可以为其添加了很多新增的功能,将其命名为MessageTransfer。
一.核心优点
1.高度解耦
生命周期与页面实例周期相隔离
可实现跨组件间通讯
业务无关,内部只关心block代码执行
2.灵活定制
每一条信息在发送的时候可以设置同步或异步执行
支持消息的内部处理操作,内部处理操作后将结果返回
一个消息有多个接收者时可以通过优先级排序执行。(同步情况下)
同一个消息同一个实例可以实现多个block,并且可以是普通block+处理block
3.使用简便
接口清晰符合逻辑设定,block挂在一起,代码聚合式管理
内部实现一些连带操作,使用时或修改时都只用修改一处,以前则需要需求一变改多处
严格把控循环引用无法释放等情况,内部实现了观察者delloc时的移除
二.API设计
1.以前的API使用
// ********普通做法
// 1.一边发送 (这个通知的名字命名还需要注意统一)
[[NSNotificationCenter defaultCenter]postNotificationName:@"XXX" object:XXX userInfo:XXX];
// 2.另一边接收
[[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(xxx:) name:@"XXX" object:XXX]
// 3.还要手动去实现一个方法
XXX:
// 4.在自己方法的delloc时还要记得将观察者移除,否则会导致崩溃。
delloc: [NSNotificationCenter defaultCenter]removeObserver
2.MessageTransfer API设计
// ********MessageTransfer API设计
//下面方法的复杂度由杂至简,只贴了最复杂方法的注释
/**
* add a block also add observer with priority,when msg received, and do some processing when msg received,return the results
*
* @param msg origin msg
* @param interaction include observer and priority
* @param block doing onReceived,and return the processing results
*/
- (void)listenMsg:(NSString *)msg withInteraction:(SXMessageInteraction *)interaction onReceiveAndProcessing:(MsgPosterReturnAction)block;
- (void)listenMsg:(NSString *)msg withInteraction:(SXMessageInteraction *)interaction onReceive:(MsgPosterVoidAction)block;
- (void)listenMsg:(NSString *)msg observer:(id)observer onReceiveAndProcessing:(MsgPosterReturnAction)block;
- (void)listenMsg:(NSString *)msg observer:(id)observer onReceive:(MsgPosterVoidAction)block; /**
* send a msg with a object and set this msg's excute type ,and do the block when msg on reached,
*
* @param msg origin msg
* @param object msg carry object
* @param type (async or sync default is sync)
* @param block doing on reached
*/
- (void)sendMsg:(NSString *)msg withObject:(id)object type:(SXMessageExcuteType)type onReached:(MsgPosterVoidAction)block;
- (void)sendMsg:(NSString *)msg withObject:(id)object onReached:(MsgPosterVoidAction)block;
- (void)sendMsg:(NSString *)msg onReached:(MsgPosterVoidAction)block;
三.流程结构

上图大致画出了,实例监听消息,同步消息发送时所产生的事件联动原理。 包括消息和观察者注册后的压栈存储,transfer内部对同步异步判断后所采用的不同执行策略,观察者的按优先级排序, 需要内部处理的block 通过发送者的msgObject作为入参执行block后返回值作为发送者block的入参继续执行,当一个实例销毁时,在观察者栈里将其移除。(董铂然博客园)
四.实际使用
// ********观察者A (普通监听)
[MsgTransfer listenMsg:@"DSXDSX" onReceive:^(id msgObject) {
MTLog(@"*******最普通的监听回调,参数内容%@",msgObject);
}]; // ********观察者B (复杂监听)
[MsgTransfer listenMsg:@"DSXDSX" withInteraction:[SXMessageInteraction interactionWithObserver:self priority:@(700)] onReceiveAndProcessing:^id (id dict) {
MTLog(@"*******优先级是700的block执行-参数%@",dict);
// 假设对传入的dict做了处理后返回一个字典
BOOL loginSuccess = [dict[@"pwd"] isEqualToString:@"123456"] && [dict[@"account"] isEqualToString:@"admin"];
return @{@"result":(loginSuccess?@"登录成功,即将跳转...":@"账号或密码有个不对")};
}]; // ********发送者 (同步执行)
[MsgTransfer sendMsg:@"DSXDSX" withObject:@{@"account":@"admin",@"pwd":@"123456"} type:SXMessageExcuteTypeSync onReached:^(id obj) {
if ([obj isKindOfClass:[NSDictionary class]]) {
MTLog(@"一个内部处理后的回调 *****%@",obj[@"result"]);
}else{
MTLog(@"一个普通者的回调 *****消息ID%@",obj);
}
}]; // 然后就.. 没了
大概的实现了一个登录逻辑,发送的消息中的object带上了登录信息,负责登录的类接收到了消息之后对参数进行了判断或其他处理将结果返回,这个block的返回值会作为发送者block的入参。也就是说发送登录信息的类在这个消息的block中就能够拿到登录结果。 这些都是以往的消息中心所不能做到的。
五.源码片段
#pragma mark -
#pragma mark listen recieved - (void)workingOnReceived:(NSNotification *)object{
NSString *name = object.name; SXMessageExcuteType excuteType = [[self.msgExcuteType objectForKey:name]integerValue]; NSArray *observerArray = [self.msgObserversStack valueForKey:name];
if (excuteType == SXMessageExcuteTypeSync) {
NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"_priority" ascending:NO];
observerArray = [observerArray sortedArrayUsingDescriptors:[NSArray arrayWithObject:sortDescriptor]];
} for (SXMessageObserver *obs in observerArray) {
NSArray *voidBlocks = [self.blockReceivedVoidStack valueForKey:obs.msgName];
NSArray *returnBlocks = [self.blockReceivedReturnStack valueForKey:obs.msgName]; if(voidBlocks && (voidBlocks.count > 0)){
for (id voidBlock in voidBlocks) {
if (excuteType == SXMessageExcuteTypeSync){
[self excuteWithVoidBlockDict:@{@"obs":obs,@"object":object,@"block":voidBlock}];
}else if (excuteType == SXMessageExcuteTypeAsync){
NSInvocationOperation *operation = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(excuteWithVoidBlockDict:) object:@{@"obs":obs,@"object":object,@"block":voidBlock}];
[self.msgQueue addOperation:operation];
}
}
} if (returnBlocks && (returnBlocks.count >0)){
for (id returnBlock in returnBlocks) {
if (excuteType == SXMessageExcuteTypeSync){
[self excuteWithReturnBlockDict:@{@"obs":obs,@"object":object,@"block":returnBlock}];
}else if (excuteType == SXMessageExcuteTypeAsync){
NSInvocationOperation *operation = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(excuteWithReturnBlockDict:) object:@{@"obs":obs,@"object":object,@"block":returnBlock}];
[self.msgQueue addOperation:operation];
}
}
} if(returnBlocks.count + voidBlocks.count < 1){
#if TEST || DEBUG
NSString *errormsg = [NSString stringWithFormat:@"dsxWARNING! this msg <%@> not binding Recieved block",obs.msgName];
NSLog(@"%@",errormsg);
#endif
}
}
}
这上面就是观察者的block即将执行的方法,其实原理很简单就是库里自己设置了很多的栈用来存储不能类别的block和观察者。并且以前的观察者可能是A或B或C,现在的观察者统一汇总到MessageTransfer。由这个中转站来控制观察者执行block。下面的一个方法就是前面所说的监听者把处理结果返回给发送者的block作为入参。
- (void)excuteWithReturnBlockDict:(NSDictionary *)dict{
SXMessageObserver *obs = dict[@"obs"];
NSNotification *object = dict[@"object"];
id block = dict[@"block"];
MsgPosterReturnAction returnBlockRecieved = (MsgPosterReturnAction)block;
id processingObject = returnBlockRecieved(object.object)?:returnBlockRecieved([NSObject new]);
MsgPosterVoidAction blockReached = [self.blockReachedStack valueForKey:object.name];
if (blockReached) {
// if processingObject is nil .
blockReached((processingObject?:@"processing result is nil"));
}else{
#if TEST || DEBUG
NSString *errormsg = [NSString stringWithFormat:@"dsxWARNING! this msg <%@> not binding Reached block",obs.msgName];
NSLog(@"%@",errormsg);
#endif
}
}
六.局限性
当然写的这个messageTransfer的使用也是有一些局限性: 如果一个实例中有多个block,那这些block的优先级就会以最后一次设置的为准,同一个实例只能有一个优先级, 不同优先级的block按顺序执行是针对不能实例的观察者而言的。原本想设置的是实例内也能设置顺序优先级,但是发现这样会让数据结构过于复杂,并且通知中心也没这么细的粒度,他们都是对于同一个消息只会绑定一个方法。所以这个局限性暂时还没遇到无法实现的需求。 还有一点局限性就是观察者的移除过程,虽然内部有观察者移除的方法不需要每一个观察者都在自己的delloc移除了,但是也需要一个触发的方法,就是在所有类的父类的delloc发送一条消息即可,如果你说我们有父类我父类就是UIViewController,那就没办法了 只能你用到时就在子类的delloc发消息了。
// 父类的delloc
- (void)dealloc
{
[[NSNotificationCenter defaultCenter]postNotificationName:@"SXMsgRemoveObserver" object:self];
}
#pragma mark -
#pragma mark remove observer
- (void)removeObserverInObserverStack:(NSNotification *)no
{
id observer = no.object;
if (![self.obsIndex containsObject:@([observer hash])]) return; NSLog(@"移除观察者--%ld",[observer hash]);
[self.msgObserversStack enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id _Nonnull obj, BOOL * _Nonnull stop) {
NSMutableArray *marray = (NSMutableArray *)obj; id temobj = nil;
for (SXMessageObserver *obs in marray) {
if ([@(obs.objectID) isEqual:@([observer hash])]) {
temobj = obs;
}
}
[marray removeObject:temobj];
[self.msgObserversStack setObject:marray forKey:key];
}];
}
附图:
下面有两个调试中的log打印,从中可以看出:发送者和监听者的block都可以执行;能执行普通的bock和带返回值的可处理的block;同一个实例可以绑定多个block;同一个类名不同的实例的block也不会发生冲突;同步和异步执行良好没有漏掉log打印。
同步执行

异步执行

这个库暂时还在完善中,后续会多些优化,判空,提示,断言等。
如果有兴趣的可以看源码 https://github.com/dsxNiubility/SXMessageTransfer
iOS通知中心升级 -可设置按优先级执行block的更多相关文章
- iOS通知中心
iOS通知中心 它是iOS程序内部的一种消息广播机制,通过它,可以实现无引用关系的对象之间的通信.通知中心他是基于观察者模式,它只能进行程序内部通信,不能跨应用程序进程通信. 当通知中心接受到消息后会 ...
- iOS 通知中心扩展制作初步-b
涉及的 Session 有 Creating Extensions for iOS and OS X, Part 1 Creating Extensions for iOS and OS X, Par ...
- iOS 通知中心 NSNotificationCenter
iOS开发中,每个app都有一个通知中心,通知中心可以发送和接收通知. 在使用通知中心 NSNotificationCenter之前,先了解一下通知 NSNotification. NSNotific ...
- QF——iOS通知中心(NotificationCener)
前面我们讲iOS不同界面间传值的时候,说过可以通过通知中心进行传值.那到底什么是通知中心,他是如何实现传值的呢? NSNotificationCenter是单例的,只提供了一个唯一的实例化入口,在整个 ...
- iOS监听模式系列之通知中心
补充--通知中心 对于很多初学者往往会把iOS中的本地通知.推送通知和iOS通知中心的概念弄混.其实二者之间并没有任何关系,事实上它们都不属于一个框架,前者属于UIKit框架,后者属于Foundati ...
- 通知中心 NSNotificationCenter 的简单使用方法
NSNotificationCenter(通知中心) [注意]需再dealloc中移除观察者 获取通知中心单例对象 NSNotificationCenter *center=[NSNotifi ...
- iOS开发之通知中心(NSNotificationCenter)
前言 面向对象的设计思想是把行为方法封装到每一个对象中,以用来增加代码的复用性.正是这种分散封装,增加了对象之间的相互关联,总是有很多的对象需要彼此了解以及相互操作! 一个简单示例说明这种交互产生的对 ...
- IOS回调机制——代理,通知中心以及Block
Xcode5.0正式版 IOS7和Xcode5正式版在昨天正式可以下载.IOS7不多说了,交互设计,界面风格,操作的简化程度都属于比较领先的水平. 这里来说说Xcode5正式版,和以前的Xcode5测 ...
- iOS基础 - 通知中心(NSNotificationCenter)
通知中心(NSNotificationCenter) 每一个应用程序都有一个通知中心(NSNotificationCenter)实例,专门负责协助不同对象之间的消息通信 任何一个对象都可以向通知中心发 ...
随机推荐
- ASP.NET MVC5 网站开发实践(二) Member区域 - 文章管理架构
上次把member的用户部分完成,现在开始做文章管理部分.文章部分根据涉及显示现实文章列表,发布文章,修改文章,删除文章等功能.最终的实现目标是使用权限来控制用户是否能进行相应操作,管理员权限的会显示 ...
- Android okHttp网络请求之Retrofit+Okhttp+RxJava组合
前言: 通过上面的学习,我们不难发现单纯使用okHttp来作为网络库还是多多少少有那么一点点不太方便,而且还需自己来管理接口,对于接口的使用的是哪种请求方式也不能一目了然,出于这个目的接下来学习一下R ...
- Ubuntu杂记——Ubuntu下Eclipse安装Maven问题
转:在线安装maven插件问题:Cannot complete the install because one or more required items could not be found. 使 ...
- 修改USB固件库的Customer_HID例程
我用的是神州三号开发板子,板子的USB模块原理图为: 配置端口G的11号引脚为usb的使能引脚,按理来说应该是开漏输出的(看了很多的修改代码都是这个模式),不过就是不能使能usb,只能配置成推挽的才行 ...
- 站在巨人的肩膀上---重新自定义 android- ExpandableListView 收缩类,实现列表的可收缩扩展
距离上次更新博客,时隔略长,诸事繁琐,赶在去广州答辩之前,分享下安卓 android 中的一个 列表收缩 类---ExpandableListView 先上效果图: 如果想直接看实现此页面的代码请下滑 ...
- 数据库SQL Service 2014中文版的安装和配置教程
一.我的电脑环境 1.windows8.1(64位) 2.之前电脑没有安装数据库的软件 二.装机之前准备(我这儿提供百度云保存和下载) 1.下载一个“Sql service 2014中文版” http ...
- 原创:从零开始,微信小程序新手入门宝典《一》
为了方便大家了解并入门微信小程序,我将一些可能会需要的知识,列在这里,让大家方便的从零开始学习:一:微信小程序的特点张小龙:张小龙全面阐述小程序,推荐通读此文: 小程序是一种不需要下载.安装即可使用的 ...
- POST方式提交表单时,后台接受实体如果继承了父类,将无法映射表单对应数据
引言 刚才在做一个post提交表单时,我在表单里放了几个隐藏域用来存放数据,表单name属性和后台实体属性签名保持一致.只是后台Action参数包含继承关系,所以无法获取到表单对应的值.刚开始一直纳闷 ...
- SpringMVC 参数注入
写一个web service, 总是400. 说是request有问题,server不识别.然而检查了很多次都没问题.最终问题指向spring对参数的解析和注入. 一个controller中可以自定义 ...
- .NET环境下基于RBAC的访问控制
.NET环境下基于RBAC的访问控制 Access Control of Application Based on RBAC model in .NET Environment 摘 要:本文从目前信息 ...