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)实例,专门负责协助不同对象之间的消息通信 任何一个对象都可以向通知中心发 ...
随机推荐
- 前端学PHP之面向对象系列第四篇——关键字
× 目录 [1]public [2]protected [3]private[4]final[5]static[6]const[7]this[8]self[9]parent 前面的话 php实现面向对 ...
- Oracle_SQL函数-分组函数
分组函数 什么是分组函数 分组函数作用于一组数据,并对一组数据返回一个值 组函数类型:主要有6种 AVG - 平均 COUNT - 计数 MAX - 最大 MIN - 最小 SUM - 求和 STDD ...
- iOS开发之手势识别
感觉有必要把iOS开发中的手势识别做一个小小的总结.在上一篇iOS开发之自定义表情键盘(组件封装与自动布局)博客中用到了一个轻击手势,就是在轻击TextView时从表情键盘回到系统键盘,在TextVi ...
- JDBC与JAVA数据库编程
一.JDBC的概念 1. JDBC (Java DataBase Connectivity) Java数据库连接 a) 主要提供java数据库应用程序的API支持 2. JDBC的主要功能 a) 创建 ...
- 小菜学习Winform(一)贪吃蛇2
前言 上一篇<小菜学习Winform(一)贪吃蛇>中实现了简单版的贪吃蛇,在文章末也提到需要优化的地方,比如使用oo.得分模式.速度加快模式和减少界面重绘.因为是优化篇,实现方式上一篇有, ...
- Myeclipse中导入项目后java类中汉字注释出现乱码问题(已解决)
今天重装系统,安装了新的Myeclipse后,导入之前的项目后,,出现了乱码问题.乱码问题主要是java类中的注释,而jsp页面中汉字却完好如初: 右键项目,查看项目的编码格式,UTF-8,把java ...
- Front End Developer Questions 前端开发人员问题(二)
问题来源:http://markyun.github.io/2015/Front-end-Developer-Questions/ 二.CSS 1.介绍一下标准的CSS的盒子模型?与低版本IE的盒子模 ...
- WebGIS中矢量切图的初步研究
文章版权由作者李晓晖和博客园共有,若转载请于明显处标明出处:http://www.cnblogs.com/naaoveGIS/. 1.背景 在GIS领域,金字塔技术一直是一个基础性技术,WMTS规范专 ...
- 小div布局之卡片堆叠(card-stacking)
前端的页面布局和各种效果真是让人眼花缭乱,公司的设计师恨不得在一个网站上把前端的布局和样式效果都用一遍. 如何实现下面这种布局效果?我给这种布局效果起了个名字,叫做小div布局之卡片堆叠.然后我百度了 ...
- Spring整合Ehcache管理缓存
前言 Ehcache 是一个成熟的缓存框架,你可以直接使用它来管理你的缓存. Spring 提供了对缓存功能的抽象:即允许绑定不同的缓存解决方案(如Ehcache),但本身不直接提供缓存功能的实现.它 ...