/GLPubSub

A wrapper of NSNotificationCenter to make pub sub easier

Primary LanguageObjective-CMIT LicenseMIT

GLPubSub

README in Chinese (中文说明)

GLPubSub is a wrapper of NSNotificationCenter to make pub sub easier. It's a category on NSObject, so it can be called on any subclass of NSObject.

Installation

CocoaPods

With CocoaPods, you can simply:

  • Add following dependency in your Podfile:
pod "GLPubSub", "~> 1.0"
  • Run pod install to install GLPubSub.

Source File

If you're not using CocoaPods to manage libraries, you can also import source file directly.

  • Download latest code and unzip.
  • Import NSObject+GLPubSub.h and NSObject+GLPubSub.m into your project. Remember to tick "Copy items if needed" when importing.

Usage

Because GLPubSub is based on NSNotificationCenter and registered on [NSNotificationCenter defaultCenter], system notifications are also supported, e.g. UIApplicationDidEnterBackgroundNotification, UIApplicationDidBecomeActiveNotification, UITextFieldTextDidChangeNotification etc. But you will lose userInfo of the notification.

Set PubSub Queue

GLPubSub is built on top of -addObserverForName:object:queue:usingBlock: method of NSNotificationCenter. You can set the queue passed into this method by calling +setPubSubQueue: of NSObject.

The PubSub queue is nil by default, which means event callbacks will be triggered on the queue where the notification is posted. You can explicitly set the queue to [NSOperationQueue maniQueue] to make all callbacks triggered on main thread.

[NSObject setPubSubQueue:[NSOperationQueue mainQueue]];

Subscribe to Events with Selector

Most of the time, we use self as subscriber:

[self subscribe:@"YourEventName" selector:@selector(yourEventHandler)];

You can subscribe to event published by some specified object:

[self subscribe:@"YourEventName" obj:somePublisher selector:@selector(yourEventHandler)];

If you want your handler only be triggered once, you can use:

[self subscribeOnce:@"YourEventName" selector:@selector(yourEventHandler)];

Then after triggered, the event will be automatically unsubscribed.

Your selector can defined accepting one parameter, if so, a GLEvent object representing the event will be passed into your selector.

@interface GLEvent : NSObject
@property (nonatomic, copy) NSString *name;
@property (nonatomic, retain) id obj;
@property (nonatomic, retain) id data;

Where, name is the event name, obj is the publisher who triggered the event and data is additional data of the event.

Subscribe to Events with Handler Block

All the methods are similar to those above, only with selector replaced by handler block.

GLEventHandler is defined as:

typedef void (^GLEventHandler)(GLEvent *event);

So you can subscribe to some event using syntax like:

__weak __typeof__(self) weakSelf = self;
[self subscribe:UIApplicationDidEnterBackgroundNotification handler:^(GLEvent *event) {
    __strong __typeof__(weakSelf) strongSelf = weakSelf;
    [strongSelf appDidEnterBackground];
}];

Weakifying self here is important to avoid retain cycle. Corresponding to methods with selector, there're 4 methods can be used with handler block:

- (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;

Unsubscribe

Unsubscribe one event:

- (void)unsubscribe:(NSString *)eventName;

Unsubscribe all subscribed events:

- (void)unsubscribeAll;

Although the observers will be deallocated when the instance deallocate, it's still recommended to unsubscribe manually, for example in -dealloc method, or sometimes in -viewDidDisappear method, according to your requirement.

- (void)dealloc
{
    [self unsubscribeAll];
}

Publish Event

You can simply publish an event without additional data:

[self publish:@"YourEventName"];

or with additional data, most of the time a NSDictionary object in practice:

[self publish:@"YourEventName" data:@{@"key": value}]

Retain Cycle

Since all observers are retained by self, there will be retain cycle if your block retains self. So you have to weakify self as mentioned above. We highly recommend doing weakify/strongify with EXTScope in libextobjc, with the library, your code would look like:

@weakify(self);
[self subscribe:UIApplicationDidEnterBackgroundNotification handler:^(GLEvent *event) {
    @strongify(self);
    [self appDidEnterBackground];
}];

License

GLPubSub is available under the MIT license. See the LICENSE file for more info.


GLPubSub (Chinese)

README in English

GLPubSub 是 NSNotificationCenter 的封装,目标是简化 iOS 开发中的发布订阅模式。因为是 NSObject 的 Category,所以可以在任意 NSObject 的子类上使用。

安装

CocoaPods

如果通过 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}]

循环引用

因为所有生成的观察者都会被 self 引用,所以当你的 block 引用 self 的时候就会形成循环引用导致实例无法被释放,所以你必须 weakify self。强烈推荐用 libextobjc 中的 EXTScope 来做 weakify/strongify:

@weakify(self);
[self subscribe:UIApplicationDidEnterBackgroundNotification handler:^(GLEvent *event) {
    @strongify(self);
    [self appDidEnterBackground];
}];

License

GLPubSub 基于 MIT 协议开源,详见 LICENSE 文件。