Glow iOS 中的訂閱釋出模式
前言
我們計劃通過 blog 和開源的方式,分享一些我們在開發 Glow 和 Nurture 的過程中積累的開發經驗。GLPubSub 作為開源計劃的第一步,是一個程式碼量較少,但很實用的 Category。
Notification 作為釋出訂閱模式(觀察者模式)的一種,在 iOS App 的開發過程中很常見,GLPubSub 是 NSNotificationCenter
的封裝,目標是簡化 iOS 開發中的釋出訂閱模式。因為是 NSObject
的 Category,所以可以在任意 NSObject
的子類上使用。
安裝
CocoaPods
如果通過 CocoaPods 管理第三方依賴,你可以:
- 在
Podfile
裡新增以下依賴:
pod "GLPubSub", "~> 1.0"
- 執行
pod install
來安裝 GLPubSub
原始檔
如果你的專案沒有用 CocoaPods 來管理第三方依賴,你也可以直接匯入原始檔。
- 下載最新程式碼並解壓
- 匯入
NSObject+GLPubSub.h
和NSObject+GLPubSub.m
到你的工程,記得在匯入時勾選 "Copy items if needed"
使用
因為 GLPubSub 是基於 NSNotificationCenter
並註冊在 [NSNotificationCenter defaultCenter]
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}]
迴圈引用
因為所有生成的觀察者都會被 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 檔案。