【iOS印象】GLPubSub 源码阅读笔记

栏目: IOS · 发布时间: 6年前

内容简介:@(iOS知行路)[源码阅读|iOS开发|NSNotification|iOS印象]如果你的项目没有用 CocoaPods 来管理第三方依赖,你也可以直接导入源文件。

@(iOS知行路)[源码阅读|iOS开发|NSNotification|iOS印象]

GLPubSubNSNotificationCenter 的封装,目标是简化 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.hNSObject+GLPubSub.m 到你的工程,记得在导入时勾选 "Copy items if needed"

因为 GLPubSub 是基于 NSNotificationCenter 并注册在 [NSNotificationCenter defaultCenter] 的,所以 GLPubSub 也支持大部分系统通知,例如 UIApplicationDidEnterBackgroundNotificationUIApplicationDidBecomeActiveNotificationUITextFieldTextDidChangeNotification 等等,但是转发的过程中会丢弃系统通知的 userInfo 字段。

设置 PubSub 的队列

GLPubSub 主要基于 NSNotificationCenter -addObserverForName:object:queue:usingBlock: 方法。你可以调用 NSObject+setPubSubQueue: 方法来设置传入该方法的 queue

默认传入的 queuenil ,这意味着所有事件会在发布通知的线程中被执行。你可以手动设置为 [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}]
复制代码

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Agile Web Application Development with Yii 1.1 and PHP5

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》 这本书的介绍吧!

在线进制转换器
在线进制转换器

各进制数互转换器

图片转BASE64编码
图片转BASE64编码

在线图片转Base64编码工具

Markdown 在线编辑器
Markdown 在线编辑器

Markdown 在线编辑器