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)实例,专门负责协助不同对象之间的消息通信 任何一个对象都可以向通知中心发 ...
随机推荐
- JavaScript的Tab切换
在网页设计中经常要用到tab切换,遂整理了一下常用的两种方法. 先看一下示例代码: HTML: <!doctype html> <html lang="en"&g ...
- vmware网卡设置详解
转载请注明出处!本文连接及作者.不得用于商业用途! http://hi.baidu.com/quantumcloud/blog/item/9156a6c584996c179c163d5b.html B ...
- 跨域post请求实现方案小结--转
[名词解释] 跨域:https://developer.mozilla.org/en-US/docs/JavaScript/Same_origin_policy_for_JavaScript 同源策略 ...
- Cesium原理篇:6 Renderer模块(2: Texture)
Texture也是WebGL中重要的概念,使用起来也很简单.但有句话叫大道至简,如果真的想要用好纹理,里面的水其实也是很深的.下面我们来一探究竟. 下面是WebGL中创建一个纹理的最简过程: var ...
- 【JVM】JVM系列之垃圾回收(二)
一.为什么需要垃圾回收 如果不进行垃圾回收,内存迟早都会被消耗空,因为我们在不断的分配内存空间而不进行回收.除非内存无限大,我们可以任性的分配而不回收,但是事实并非如此.所以,垃圾回收是必须的. 二. ...
- 4.Struts2转向类型详解
struts2中提供了多种视图转向类型,类型由type属性指定,如: dispatcher:请求转发(默认值) redirect:重定向到页面 redirectAction:重定向到Action pl ...
- Quartz.net 开源job调度框架(一)
Quartz.NET是一个开源的作业调度框架,非常适合在平时的工作中,定时轮询数据库同步,定时邮件通知,定时处理数据等. Quartz.NET允许开发人员根据时间间隔(或天)来调度作业.它实现了作业和 ...
- FPGA的引脚VCCINT 、VCCIO VCCA
首先是看到FPGA在配置的时候有三种不同的电VCCINT .VCCIO VCCA,于是就查了下有什么不同: FPGA一般会有许多引脚,那它们都有什么用呢? VCCINT为施加于 FPGA 内核逻辑的电 ...
- 《Pro ASP.NET MVC 4》异常整理
最近在和同学一起研究毕业设计,准备模仿<Pro ASP.NET MVC 4>里面的SportsStore设计模式和测试驱动开发. 由于和书中VS版本不同,发现不少问题,在此总结. 用户代码 ...
- Error LNK1104 cannot open file 'libboost_system-vc140-mt-gd-1_58.lib'
I had a similar problem when trying to use boost unit testing in Visual Studio 2015 (Community Editi ...