内容简介:@(iOS知行路)[源码阅读|iOS开发|NSNotification|iOS印象]如果你的项目没有用 CocoaPods 来管理第三方依赖,你也可以直接导入源文件。
@(iOS知行路)[源码阅读|iOS开发|NSNotification|iOS印象]
GLPubSub
是 NSNotificationCenter
的封装,目标是简化 iOS 开发中的发布订阅模式。该库通过添加 NSObject 的 Category,使得所有对象都可以方便地使用其订阅或发布通知。
源码阅读
-
GLEvent 类,传递的事件对象,包含
事件名称
、事件发布者
、事件附带的额外数据
/** `GLEvent` is the event object passed into handler block. */ @interface GLEvent : NSObject /** * Name of the event */ @property (nonatomic, copy) NSString *name; /** * The object triggering the event */ @property (nonatomic, retain) id obj; /** * Additional data of the event */ @property (nonatomic, retain) id data; /** * Init `GLEvent` with `name`, `obj` and `data`. * * @param name Name of the event * @param obj The object triggering the event * @param data Additional data of the event * * @return Initialized `GLEvent` */ - (id)initWithName:(NSString *)name obj:(id)obj data:(id)data; @end 复制代码
-
通过为
NSObject
添加 Category,增加订阅/取消订阅
、发布
事件等方法,以及设置事件处理的处理队列
@interface NSObject (GLPubSub) #pragma mark - Class Methods /** * Set the queue to handle events. If not set or set to `nil, events will be handled on the queue where the notification is posted. Otherwise, all the events will be handled on the set queue. * * @param queue The queue on which to handle events. */ + (void)setPubSubQueue:(NSOperationQueue *)queue; #pragma mark - Publish Methods /** * Publish an event without additional data. * * @param name Event name to publish. * * @see -publish:data: */ - (void)publish:(NSString *)name; /** * Publish an event with additional data. * * @param name Event name to publish. * @param data Additional data. * * @see -publish: */ - (void)publish:(NSString *)name data:(id)data; #pragma mark - Subscribe Methods with Selector /** * Subscribe an event with selector. * * @param eventName Event name to subscribe. * @param selector Selector to handle the event. * * @return An opaque object to act as the observer. * * @see -subscribe:obj:selector: * @see -subscribeOnce:selector: * @see -subscribeOnce:obj:selector: */ - (id)subscribe:(NSString *)eventName selector:(SEL)selector; /** * Subscribe an event from a specified object with selector. * * @param eventName Event name to subscribe. * @param object Publisher to subscribe from. * @param selector Selector to handle the event. * * @return An opaque object to act as the observer. * * @see -subscribe:selector: * @see -subscribeOnce:selector: * @see -subscribeOnce:obj:selector: */ - (id)subscribe:(NSString *)eventName obj:(id)obj selector:(SEL)selector; /** * Subscribe an event only once with selector. * * @param eventName Event name to subscribe. * @param selector Selector to handle the event. * * @return An opaque object to act as the observer. * * @see -subscribe:selector: * @see -subscribe:obj:selector: * @see -subscribeOnce:obj:selector: */ - (id)subscribeOnce:(NSString *)eventName selector:(SEL)selector; /** * Subscribe an event from a specified object only once with selector. * * @param eventName Event name to subscribe. * @param object Publisher to subscribe from. * @param selector Selector to handle the event. * * @return An opaque object to act as the observer. * * @see -subscribe:selector: * @see -subscribe:obj:selector: * @see -subscribeOnce:selector: */ - (id)subscribeOnce:(NSString *)eventName obj:(id)obj selector:(SEL)selector; #pragma mark - Subscribe Methods with Handler Block /** * Subscribe an event with handler block. * * @param eventName Event name to subscribe. * @param handler Handler block to handle the event. * * @return An opaque object to act as the observer. * * @see -subscribe:obj:handler: * @see -subscribeOnce:handler: * @see -subscribeOnce:obj:handler: */ - (id)subscribe:(NSString *)eventName handler:(GLEventHandler)handler; /** * Subscribe an event from a specified object with handler block. * * @param eventName Event name to subscribe. * @param obj Publisher to subscribe from. * @param handler Handler block to handle the event. * * @return An opaque object to act as the observer. * * @see -subscribe:handler: * @see -subscribeOnce:handler: * @see -subscribeOnce:obj:handler: */ - (id)subscribe:(NSString *)eventName obj:(id)obj handler:(GLEventHandler)handler; /** * Subscribe an event only once with handler block. * * @param eventName Event name to subscribe. * @param handler Handler block to handle the event. * * @return An opaque object to act as the observer. * * @see -subscribe:handler: * @see -subscribe:obj:handler: * @see -subscribeOnce:obj:handler: */ - (id)subscribeOnce:(NSString *)eventName handler:(GLEventHandler)handler; /** * Subscribe an event from a specified object only once with handler block. * * @param eventName Event name to subscribe. * @param obj Publisher to subscribe from. * @param handler Handler block to handle the event. * * @return An opaque object to act as the observer. * * @see -subscribe:handler: * @see -subscribe:obj:handler: * @see -subscribeOnce:handler: */ - (id)subscribeOnce:(NSString *)eventName obj:(id)obj handler:(GLEventHandler)handler; #pragma mark - Unsubscribe Methods /** * Unsubscribe speficied event for current instance. * * @param eventName Name of event to unsubscribe. * * @see -unsubscribeAll */ - (void)unsubscribe:(NSString *)eventName; /** * Unsubscribe all events for current instance. * * @see -unsubscribe: */ - (void)unsubscribeAll; @end 复制代码
- 发布事件的实现
- (void)publish:(NSString *)name data:(id)data { // 通知中心发出通知,并将事件对象放入`userInfo` NSDictionary *userInfo = nil; if (data != nil) { userInfo = @{kGLPubSubDataKey: data}; } [[NSNotificationCenter defaultCenter] postNotificationName:name object:self userInfo:userInfo]; } 复制代码
- 订阅事件的实现
- (id)subscribe:(NSString *)eventName obj:(id)obj selector:(SEL)selector { // 用于判断`selector`的参数个数 NSMethodSignature *sig = [self methodSignatureForSelector:selector]; // The hidden arguments self and _cmd (of type SEL) are at indices 0 and 1; method-specific arguments begin at index 2 BOOL passEventObj = ([sig numberOfArguments] == 3); __weak __typeof__(self) weakSelf = self; return [self subscribe:eventName obj:obj handler:^(GLEvent *event) { __strong __typeof__(weakSelf) strongSelf = weakSelf; if (passEventObj) { [strongSelf performSelector:selector withObject:event]; } else { [strongSelf performSelector:selector]; } }]; } - (id)subscribe:(NSString *)eventName obj:(id)obj handler:(GLEventHandler)handler { // 通知中心通过添加观察者,并在block中生成事件进行转发 id observer = [[NSNotificationCenter defaultCenter] addObserverForName:eventName object:obj queue:_pubSubQueue usingBlock:^(NSNotification *note) { GLEvent *event = [[GLEvent alloc] initWithName:eventName obj:note.object data:[note.userInfo objectForKey:kGLPubSubDataKey]]; handler(event); }]; // 添加观察者对象至该事件的观察者数组,用于取消订阅事件 NSMutableDictionary *subscriptions = (NSMutableDictionary *)objc_getAssociatedObject(self, &kGLPubSubSubscriptionsKey); if (!subscriptions) { subscriptions = [[NSMutableDictionary alloc] init]; objc_setAssociatedObject(self, &kGLPubSubSubscriptionsKey, subscriptions, OBJC_ASSOCIATION_RETAIN_NONATOMIC); } NSMutableSet *observers = [subscriptions objectForKey:eventName]; if (!observers) { observers = [[NSMutableSet alloc] init]; [subscriptions setObject:observers forKey:eventName]; } [observers addObject:observer]; return observer; } 复制代码
- 取消事件的实现
- (void)unsubscribe:(NSString *)eventName { NSMutableDictionary *subscriptions = (NSMutableDictionary *)objc_getAssociatedObject(self, &kGLPubSubSubscriptionsKey); if (!subscriptions) return; NSMutableSet *observers = [subscriptions objectForKey:eventName]; if (observers) { // 通知中心移除所有该事件的观察者 for (id observer in observers) { [[NSNotificationCenter defaultCenter] removeObserver:observer]; } // 订阅数组中移除该事件观察者数组对象 [subscriptions removeObjectForKey:eventName]; } } 复制代码
附:安装与使用
CocoaPods
-
在
Podfile
里添加以下依赖:
pod "GLPubSub", "~> 1.0" 复制代码
-
运行
pod install
来安装 GLPubSub
源文件
如果你的项目没有用 CocoaPods 来管理第三方依赖,你也可以直接导入源文件。
- 下载 最新代码 并解压
-
导入
NSObject+GLPubSub.h
和NSObject+GLPubSub.m
到你的工程,记得在导入时勾选 "Copy items if needed"
因为 GLPubSub 是基于 NSNotificationCenter
并注册在 [NSNotificationCenter defaultCenter]
的,所以 GLPubSub 也支持大部分系统通知,例如 UIApplicationDidEnterBackgroundNotification
, UIApplicationDidBecomeActiveNotification
, UITextFieldTextDidChangeNotification
等等,但是转发的过程中会丢弃系统通知的 userInfo
字段。
设置 PubSub 的队列
GLPubSub 主要基于 NSNotificationCenter
的
-addObserverForName:object:queue:usingBlock:
方法。你可以调用 NSObject
的 +setPubSubQueue:
方法来设置传入该方法的 queue
。
默认传入的 queue
为 nil
,这意味着所有事件会在发布通知的线程中被执行。你可以手动设置为 [NSOperationQueue maniQueue]
使得所有事件在主线程被触发:
[NSObject setPubSubQueue:[NSOperationQueue mainQueue]]; 复制代码
通过 Selector 订阅事件
大部分时候,我们用 self
作为订阅者:
[self subscribe:@"YourEventName" selector:@selector(yourEventHandler)]; 复制代码
你也可以指定事件的发布者:
[self subscribe:@"YourEventName" obj:somePublisher selector:@selector(yourEventHandler)]; 复制代码
如果你希望你的方法只触发一次,你可以用:
[self subscribeOnce:@"YourEventName" selector:@selector(yourEventHandler)]; 复制代码
这样当该事件被触发后,就会自动取消订阅。
你的方法可以接受一个 GLEvent
参数,该参数包含了被触发事件的相关信息。
@interface GLEvent : NSObject @property (nonatomic, copy) NSString *name; @property (nonatomic, retain) id obj; @property (nonatomic, retain) id data; 复制代码
name
是事件名, obj
是发布者, data
是附加信息。
通过 Block 订阅事件
方法与上面通过 selector 订阅的方法类似:
GLEventHandler
定义如下:
typedef void (^GLEventHandler)(GLEvent *event); 复制代码
所以你可以如下用 block 订阅一个事件:
__weak __typeof__(self) weakSelf = self; [self subscribe:UIApplicationDidEnterBackgroundNotification handler:^(GLEvent *event) { __strong __typeof__(weakSelf) strongSelf = weakSelf; [strongSelf appDidEnterBackground]; }]; 复制代码
这里的 weak 化是为了避免循环引用。对应于前面 selector 的方法,用 block 也有 4 种调用方法:
- (id)subscribe:(NSString *)eventName handler:(GLEventHandler)handler; - (id)subscribe:(NSString *)eventName obj:(id)obj handler:(GLEventHandler)handler; - (id)subscribeOnce:(NSString *)eventName handler:(GLEventHandler)handler; - (id)subscribeOnce:(NSString *)eventName obj:(id)obj handler:(GLEventHandler)handler; 复制代码
取消订阅
取消订阅某个事件:
- (void)unsubscribe:(NSString *)eventName; 复制代码
取消订阅所有事件:
- (void)unsubscribeAll; 复制代码
虽然当实例被销毁时,存在 associated object 中的观察者也都会被销毁,但还是建议手动取消订阅,如根据不同需求,在 -dealloc
或 -viewDidDisappear
方法中取消订阅。
- (void)dealloc { [self unsubscribeAll]; } 复制代码
发布事件
你可以简单地发布一个事件:
[self publish:@"YourEventName"]; 复制代码
也可以附带一些数据,很多时候我们会传入一个 NSDictionary
来附带更多结构化的数据:
[self publish:@"YourEventName" data:@{@"key": value}] 复制代码
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- springCloud/印象
- 印象深刻的Ruby例子
- 浅谈 记一次 import 打包 印象误区
- 苹果WWDC印象:凶残的节奏、低调亮剑AI
- 服务通知:印象笔记PC端Markdown取消支持HTML标签解析功能
- 消除NLP中的刻板印象:程序员之于男性=家政人员之于女性?
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Python Web开发:测试驱动方法
Harry J.W. Percival / 安道 / 人民邮电出版社 / 2015-10 / 99
本书从最基础的知识开始,讲解Web开发的整个流程,展示如何使用Python做测试驱动开发。本书由三个部分组成。第一部分介绍了测试驱动开发和Django的基础知识。第二部分讨论了Web开发要素,探讨了Web开发过程中不可避免的问题,及如何通过测试解决这些问题。第三部分探讨了一些高级话题,如模拟技术、集成第三方插件、Ajax、测试固件、持续集成等。本书适合Web开发人员阅读。一起来看看 《Python Web开发:测试驱动方法》 这本书的介绍吧!