简单介绍下,这是需求驱动中发现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的更多相关文章

  1. iOS通知中心

    iOS通知中心 它是iOS程序内部的一种消息广播机制,通过它,可以实现无引用关系的对象之间的通信.通知中心他是基于观察者模式,它只能进行程序内部通信,不能跨应用程序进程通信. 当通知中心接受到消息后会 ...

  2. iOS 通知中心扩展制作初步-b

    涉及的 Session 有 Creating Extensions for iOS and OS X, Part 1 Creating Extensions for iOS and OS X, Par ...

  3. iOS 通知中心 NSNotificationCenter

    iOS开发中,每个app都有一个通知中心,通知中心可以发送和接收通知. 在使用通知中心 NSNotificationCenter之前,先了解一下通知 NSNotification. NSNotific ...

  4. QF——iOS通知中心(NotificationCener)

    前面我们讲iOS不同界面间传值的时候,说过可以通过通知中心进行传值.那到底什么是通知中心,他是如何实现传值的呢? NSNotificationCenter是单例的,只提供了一个唯一的实例化入口,在整个 ...

  5. iOS监听模式系列之通知中心

    补充--通知中心 对于很多初学者往往会把iOS中的本地通知.推送通知和iOS通知中心的概念弄混.其实二者之间并没有任何关系,事实上它们都不属于一个框架,前者属于UIKit框架,后者属于Foundati ...

  6. 通知中心 NSNotificationCenter 的简单使用方法

    NSNotificationCenter(通知中心)   [注意]需再dealloc中移除观察者   获取通知中心单例对象 NSNotificationCenter *center=[NSNotifi ...

  7. iOS开发之通知中心(NSNotificationCenter)

    前言 面向对象的设计思想是把行为方法封装到每一个对象中,以用来增加代码的复用性.正是这种分散封装,增加了对象之间的相互关联,总是有很多的对象需要彼此了解以及相互操作! 一个简单示例说明这种交互产生的对 ...

  8. IOS回调机制——代理,通知中心以及Block

    Xcode5.0正式版 IOS7和Xcode5正式版在昨天正式可以下载.IOS7不多说了,交互设计,界面风格,操作的简化程度都属于比较领先的水平. 这里来说说Xcode5正式版,和以前的Xcode5测 ...

  9. iOS基础 - 通知中心(NSNotificationCenter)

    通知中心(NSNotificationCenter) 每一个应用程序都有一个通知中心(NSNotificationCenter)实例,专门负责协助不同对象之间的消息通信 任何一个对象都可以向通知中心发 ...

随机推荐

  1. 【转】SQL删除重复数据方法,留着备用

    感谢孙潇楠前辈的总结,地址http://www.cnblogs.com/sunxiaonan/archive/2009/11/24/1609439.html 例如: id           name ...

  2. Android 设置对话框全屏

    1.在styles.xml中添加一个style: <style name="Dialog_Fullscreen"> <item name="androi ...

  3. scikit-learn 线性回归算法库小结

    scikit-learn对于线性回归提供了比较多的类库,这些类库都可以用来做线性回归分析,本文就对这些类库的使用做一个总结,重点讲述这些线性回归算法库的不同和各自的使用场景. 线性回归的目的是要得到输 ...

  4. iOS开发之SQLite-C语言接口规范(一)——Ready And Open Your SQLite

    为什么要搞一搞SQLite的C语言接口规范呢? 因为在做iOS开发中难免会遇到操作数据库的情况,你可以使用第三方的FMDB等,或者使用CoreData.但我们还是有必要去搞清楚如何去使用SQLite的 ...

  5. CentOS 7中如何安装mysql server

    以前一直用RHEL 6.3和6.4,系统盘里自带了mysql server,配置好yum源后,直接yum install mysql-server就可安装mysql服务器端了,最近用CentOS 7. ...

  6. 自己使用的一个.NET轻量开发结构

    aaarticlea/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIgAAABFCAIAAAAerjlvAAAE2UlEQVR4nO2a3U/bVhiH+bdyPaqpmx

  7. 附录D 安装ZooKeeper

    D.1    安装ZooKeeper D.1.1   下载ZooKeeper ZooKeeper是Apache基金会的一个开源.分布式应用程序协调服务,是Google的Chubby一个开源的实现.它是 ...

  8. (3)MEF插件系统中通信机制的设计和实现

    文章版权由作者李晓晖和博客园共有,若转载请于明显处标明出处:http://www.cnblogs.com/naaoveGIS/ 1.背景 一般的WinForm中通过C#自带的Event机制便能很好的实 ...

  9. angular.js写法不规范导致错误

    以下写法:没有明确指定module和controller,写法不规范. 更改angular.js版本会出bug. <html ng-app> <head> <title& ...

  10. [Q&A] 类Range的PasteSpecial方法无效

    环境说明: VS2013(C#) + Office2013 Bug说明: range1.Copy(Type.Missing); range2.PasteSpecial(Excel.XlPasteTyp ...