自定義NSNotification消息中心

原創(chuàng)文章轉(zhuǎn)載請注明出處,謝謝
端午想好好休息一下,于是就沒出去玩了

關(guān)于NSNotification大家一定不會陌生,所以這里我就不講關(guān)于NSNotification的具體用法了,網(wǎng)上一搜一大片。NSNotification是基于Observe Design Pattern來實(shí)現(xiàn)的,所有Register了這個消息的對象,都會收到由NSNotification發(fā)送的消息。它有一個自己的消息中心是一個單例,我們基本通過以下三個函數(shù)就可以實(shí)現(xiàn)一個基本的Observe Pattern。

// register notification
- (void)addObserver:(id)observer
           selector:(SEL)aSelector
               name:(nullable NSString *)aName
             object:(nullable id)anObject;

// post notification         
- (void)postNotification:(NSNotification *)notification;

// dealloc reomove notification
- (void)removeObserver:(id)observer;

但是系統(tǒng)的NSNotification實(shí)在是太臃腫了,想想要在所有的業(yè)務(wù)里到處散布著上面三行代碼,這顯然是不合適的。我們最理想的效果是只要存在兩個方法register和post,register中可以直接傳遞一個block來完成收到消息時觸發(fā)的事件,然后在dealloc中自動調(diào)用remove的操作。所以我們不妨先定義一下這兩個函數(shù):

+ (void)postNotificationNamed:(NSString*)notificationName
                       object:(id)object;

// default one parame in callback                 
+ (void)registerNotificationHandlerForName:(NSString *)notificationName
                                    target:(id)target
                                    action:(void(^)(id))action;                     
                       

其實(shí)我們主要的任務(wù)就是要在register函數(shù)中去動態(tài)在相應(yīng)的target中添加一個函數(shù),來執(zhí)行對應(yīng)的callback操作。

@interface NotificationHelper : NSObject
@end

@implementation NotificationHelper
- (void)justForGetTypeEncoding:(id)object {};
@end

+ (void)postNotificationNamed:(NSString *)notificationName object:(id)object {
  [[NSNotificationCenter defaultCenter] postNotificationName:notificationName
                                                      object:object];
}

+ (void)registerNotificationHandlerForName:(NSString *)notificationName
                                    target:(id)target
                                    action:(void(^)(id))action {
  SEL sel = NSSelectorFromString([NSString stringWithFormat:@"_xxx_notification_%@:",
                                  notificationName]);
  if ([target respondsToSelector:sel]) {
    NSError* error = nil;
    [target aspect_hookSelector:sel
                    withOptions:AspectPositionAfter
                     usingBlock:^(id<AspectInfo>info, NSNotification* notification) {
      action(notification.object);
    }
                          error:&error];
  } else {
    typedef void (^BlockType)(id, NSNotification *);
    BlockType block = ^(id target, NSNotification *notification) {
      action(notification.object);
    };
    BOOL success = class_addMethod([target class],
                                   sel,
                                   imp_implementationWithBlock(block),
            method_getTypeEncoding(class_getInstanceMethod([BMTBusinessUtilHelper class], 
            @selector(justForGetTypeEncoding:))));
  }
  
  [[NSNotificationCenter defaultCenter] addAutoDetachedObserver:target
                                                       selector:sel
                                                           name:notificationName
                                                         object:nil];
}

上面的思想其實(shí)就是當(dāng)我們?nèi)プ砸粋€消息的時候,我們先去判斷目標(biāo)class中是否已經(jīng)添加了這個SEL,如果沒有,我們就去動態(tài)添加這個方法到目標(biāo)class,如果有了,我們就在已存在的方法中追加這個方法。最后把新創(chuàng)建的方法注冊到消息中。大致的思路就是這樣,剩下的就是關(guān)于remove的操作了,思路是我們通過在調(diào)用dealloc的時候追加一個remove操作函數(shù)到里面,這樣就可以保證當(dāng)我們的class被dealloc的時候,消息能被正確的remove掉。

// not use @selector(dealloc), because ARC
[self aspect_hookSelector:NSSelectorFromString(@"dealloc") 
              withOptions:AspectPositionBefore
               usingBlock:^(id<AspectInfo>info) {
                        action();
                  } error:&error];

講到這里,基本上已經(jīng)實(shí)現(xiàn)了我們要講的一些功能,不過這里其實(shí)有一個很大的坑,不知道同學(xué)們看到這里已經(jīng)發(fā)現(xiàn)了沒有,當(dāng)我們新添加的函數(shù)已經(jīng)存在時,我們會去在已存在的函數(shù)后面追加新的action操作,這個時候考慮到如果一個class中有注冊消息的操作,但是這個class又被init了多次,那么就會出現(xiàn)我們新添加的SEL中,會有好幾個重復(fù)的action操作,如果我們的action操作只是為了更新數(shù)據(jù),那么這樣的開銷幾乎沒有,但是如果我們的action中會有大量的關(guān)于刷新界面或者其他UI的操作,其實(shí)是會額外附帶不少開銷的。所以這個就是上述實(shí)現(xiàn)的一個缺點(diǎn),我現(xiàn)在也沒有什么好的辦法來優(yōu)化這個問題,只能在代碼中盡量避免這種情況的發(fā)生,大家想到好的辦法也可以告訴我。

其實(shí)OC中的Observe Pattern可以分為三種:delegate,NSNotification,KVO,這里詳細(xì)介紹了這三種方式的區(qū)別。準(zhǔn)確來說對于model-view之間的數(shù)據(jù)同步,單純的數(shù)據(jù)更新來說KVO感覺更加適合一點(diǎn),因?yàn)橐坏┧臄?shù)據(jù)發(fā)生變化,自動所有接收消息的地方就能馬上收到通知。而NSNotification更佳適合模塊間的一些界面交互更新的操作,因?yàn)樗枰鲃尤グl(fā)送消息讓所有注冊了消息的地方都知道然后去執(zhí)行對應(yīng)的操作。所以針對上面提出的一些問題,我之后會在這個地方繼續(xù)補(bǔ)充,畢竟現(xiàn)在我還沒想出一個比較好的方案。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,695評論 19 139
  • 轉(zhuǎn)至元數(shù)據(jù)結(jié)尾創(chuàng)建: 董瀟偉,最新修改于: 十二月 23, 2016 轉(zhuǎn)至元數(shù)據(jù)起始第一章:isa和Class一....
    40c0490e5268閱讀 2,098評論 0 9
  • //Clojure入門教程: Clojure – Functional Programming for the J...
    葡萄喃喃囈語閱讀 4,071評論 0 7
  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語法,類相關(guān)的語法,內(nèi)部類的語法,繼承相關(guān)的語法,異常的語法,線程的語...
    子非魚_t_閱讀 34,842評論 18 399
  • 初衷 項(xiàng)目中與服務(wù)器端的數(shù)據(jù)交互采用的是json,服務(wù)器返回的數(shù)據(jù)格式比較統(tǒng)一,都是下面這種樣子的 {result...
    澤濟(jì)天下閱讀 945評論 3 5

友情鏈接更多精彩內(nèi)容