内容简介:@(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中的刻板印象:程序员之于男性=家政人员之于女性?
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Agile Web Application Development with Yii 1.1 and PHP5
Jeffrey Winesett / Packt Publishing / 2010-08-27
In order to understand the framework in the context of a real-world application, we need to build something that will more closely resemble the types of applications web developers actually have to bu......一起来看看 《Agile Web Application Development with Yii 1.1 and PHP5》 这本书的介绍吧!