iOS - Notification 通知

一、Notification

  • 通知中心其實是在程序內部提供了消息廣播的一種機制,它容許咱們在低程度耦合的狀況下,知足控制器與一個任意的對象進行通訊的目的。每個 iOS 程序(即每個進程)都有一個本身的通知中心,即 NSNotificationCenter 對象,該對象採用單例設計模式,能夠經過類方法 defaultCenter 得到當前進程惟一的通知中心對象。一個 NSNotificationCenter 能夠有許多的通知消息 NSNotification,對於每個 NSNotification 能夠有不少的觀察者 Observer 來接收通知。NSNotificationCenter 是 iOS 中通知中心的靈魂,由該類實現了觀察者模式,並給開發者提供了諸如註冊、刪除觀察者的接口。設計模式

  • 通知中心以同步的方法將消息轉發到全部的觀察者中,換言之 NSNotificationCenter 在發送消息後,會一直等待被調用的方法執行完畢,而後返回控制權到主函數中,再接着執行後面的功能,即這是一個同步阻塞的操做。若是咱們須要異步的處理消息,直接返回控制權,則應該使用通知隊列 NSNotificationQueue,在子線程中將通知加入到通知隊列中,在多線程程序中,通知會被分發到每個發送消息的線程中,這可能與觀察者註冊時所在的線程已經不是同一個線程。多線程

  • 任何一個對象均可以向通知中心發佈通知(NSNotification),描述本身在作什麼。其餘感興趣的對象(Observer)能夠申請在某個特定通知發佈時(或在某個特定的對象發佈通知時)收到這個通知。異步

    Notification1

  • 使用 [NSNotificationCenter defaultCenter] 發送的通知不管是在主線程仍是子線程中被註冊,觀察者註冊的選擇器方法都會在主線程中被執行。執行順序爲:main runloop -> 發送通知 -> 觀察者選 擇器方法(按照觀察者註冊的順序執行)-> 通知發送者方法中其它的操做 -> main runloop。async

  • 在子線程中使用 [NSNotificationQueue defaultQueue] 將通知加入到通知隊列中,觀察者選擇器方法就會在子線程中被執行。子線程執行順序爲:發送通知 -> 觀察者選擇器方法(按照觀察者註冊的順序同 步執行)-> 通知發送者方法中其它的操做。ide

  • 若是要在同一臺機器上進行進程間的通訊,須要使用 NSDistributedNOtificationCenter。函數

  • 優點:oop

    • 一、不須要編寫多少代碼,實現比較簡單;
    • 二、對於一個發出的通知,多個對象可以作出反應,即 1 對多的方式實現簡單;
    • 三、controller 可以傳遞 context 對象(dictionary),context 對象攜帶了關於發送通知的自定義的信息。
  • 缺點:post

    • 一、在編譯期不會檢查通知是否可以被觀察者正確的處理;
    • 二、在釋放註冊的對象時,須要在通知中心取消註冊;
    • 三、在調試的時候應用的工做以及控制過程難跟蹤;
    • 四、須要第三方來管理 controller 與觀察者對象之間的聯繫;
    • 五、controller 和觀察者須要提早知道通知名稱、UserInfo dictionary keys。若是這些沒有在工做區間定義,那麼會出現不一樣步的狀況;
    • 六、通知發出後,controller 不能從觀察者得到任何的反饋信息。
  • 目的:動畫

    • 下降兩個子系統之間的偶合度。
  • 方式:ui

    • 一個對象發送通知給通知中心,通知中心以廣播的形式通知全部的監聽者。
  • 通知和代理的區別:

    • 共同點:
      • 利用通知和代理都能完成對象之間的通訊(好比 A 對象告訴 D 對象發生了什麼事情, A 對象傳遞數據給 D 對象)
    • 不一樣點:
      • 代理 : 1 個對象只能告訴另 1 個對象發生了什麼事情。
      • 通知 :
        • 1 個對象能告訴 N 個對象發生了什麼事情, 1 個對象能得知 N 個對象發生了什麼事情。
        • 任何數量的對象均可以接收同一個消息,而不只限定於委託對象。
        • 通知系統不接受返回值。
        • 對象不須要預先定義協議方法,就能夠接收來自通知中心的消息。
        • 發佈通知的對象只負責發佈通知,不須要關心觀察者是否存在。
  • 通知中心是同步的,仍是異步的 ?

    • 同步的。當發生通知時,通知中心廣播,有可能有多個監聽者,設計上使用同步的方式,可以保證全部的監聽者都對通知做出響應,不會產生遺漏。

二、系統發送 Notification 的使用

  • 系統發送 Notification,用戶不須要手動發送通知,設置的事件觸發時,系統自動發送通知。

  • 通知中心不會保留(retain)監聽器對象,在通知中心註冊過的對象,必須在該對象釋放前取消註冊。不然,當相應的通知再次出現時,通知中心仍然會向該監聽器發送消息。由於相應的監聽器對象已經被釋放了,因此可能會致使應用崩潰。通常在監聽器銷燬以前取消註冊(如在監聽器中加入下列代碼):

    - (void)dealloc {
            // [super dealloc];  // 非 ARC 中須要調用此句
            [[NSNotificationCenter defaultCenter] removeObserver:self];
        }
  • 在註冊、移除通知時,通知名稱標示(aName)使用系統定義的標示。

  • 註冊通知(觀察者)

    • Objective-C

      [[NSNotificationCenter defaultCenter] addObserver:self 
                                                   selector:@selector(playFinished) 
                                                       name:AVPlayerItemDidPlayToEndTimeNotification 
                                                     object:nil];
    • Swift

      NSNotificationCenter.defaultCenter().addObserver(self, 
                                                 selector: #selector(ViewController.playFinished), 
                                                     name:AVPlayerItemDidPlayToEndTimeNotification, 
                                                   object: nil)
  • 移除通知(觀察者)

    • Objective-C

      [[NSNotificationCenter defaultCenter] removeObserver:self 
                                                          name:AVPlayerItemDidPlayToEndTimeNotification 
                                                        object:nil];
    • Swift

      NSNotificationCenter.defaultCenter().removeObserver(self, 
                                                         name:AVPlayerItemDidPlayToEndTimeNotification, 
                                                       object:nil)

三、自定義發送 Notification 的使用

  • 使用 [NSNotificationCenter defaultCenter] 發送的通知不管是在主線程仍是子線程中被註冊,觀察者註冊的選擇器方法都會在主線程中被執行。

  • 執行順序:main runloop -> 發送通知 -> 觀察者選擇器方法(按照觀察者註冊的順序執行)-> 通知發送者方法中其它的操做 -> main runloop

  • 通知(消息)的建立

    + (instancetype)notificationWithName:(NSString *)aName object:(nullable id)anObject;
        + (instancetype)notificationWithName:(NSString *)aName object:(nullable id)anObject 
                                                             userInfo:(nullable NSDictionary *)aUserInfo;
    
        public convenience init(name aName: String, object anObject: AnyObject?)
        public init(name: String, object: AnyObject?, userInfo: [NSObject : AnyObject]?)
    
        參數說明:
            aName    :通知名稱
            anObject :傳遞給觀察者的任意對象,通知發佈者(是誰要發佈通知)
            aUserInfo:傳遞的消息內容,自定義字典,能夠傳遞更多附加信息,一些額外的信息(通知發佈者傳遞給通知接收者的信息內容)
    • Objective-C

      // 不帶消息內容
          NSNotification *notification1 = [NSNotification notificationWithName:@"notification1" 
                                                                        object:self];                                         
      
          // 帶消息內容
          NSNotification *notification2 = [NSNotification notificationWithName:@"notification2" 
                                                                        object:self 
                                                                      userInfo:@{@"name":_name, @"age":_age}];
    • Swift

      // 不帶消息內容
          let notification1 = NSNotification(name: "notification1", 
                                           object: self)                                                                      
          // 帶消息內容
          let notification2 = NSNotification(name: "notification2", 
                                           object: self, 
                                         userInfo: ["name":name, "age":age])
  • 發送通知

    - (void)postNotification:(NSNotification *)notification;
        - (void)postNotificationName:(NSString *)aName object:(nullable id)anObject;
        - (void)postNotificationName:(NSString *)aName object:(nullable id)anObject 
                                                     userInfo:(nullable NSDictionary *)aUserInfo;
    
        public func postNotification(notification: NSNotification)
        public func postNotificationName(aName: String, object anObject: AnyObject?)
        public func postNotificationName(aName: String, object anObject: AnyObject?, 
                                                     userInfo aUserInfo: [NSObject : AnyObject]?)
    
        參數說明:
            notification:發送的通知(消息)
    
            aName       :通知名稱
            anObject    :傳遞給觀察者的任意對象,通知發佈者
            aUserInfo   :傳遞的消息內容,自定義字典,能夠傳遞更多附加信息
    • Objective-C

      // 發送建立好的消息
          [[NSNotificationCenter defaultCenter] postNotification:notification1];
      
          // 直接發送消息,不帶消息內容
          [[NSNotificationCenter defaultCenter] postNotificationName:@"notification3" 
                                                              object:self];                                           
          // 直接發送消息,帶消息內容
          [[NSNotificationCenter defaultCenter] postNotificationName:@"notification4" 
                                                              object:self 
                                                            userInfo:@{@"name":_name, @"age":_age}];
    • Swift

      // 發送建立好的消息
          NSNotificationCenter.defaultCenter().postNotification(notification1)
      
          // 直接發送消息,不帶消息內容
          NSNotificationCenter.defaultCenter().postNotificationName("notification3", 
                                                             object: self)                                            
          // 直接發送消息,帶消息內容
          NSNotificationCenter.defaultCenter().postNotificationName("notification4", 
                                                             object: self, 
                                                           userInfo: ["name":name, "age":age])
  • 註冊通知(觀察者)

    - (void)addObserver:(id)observer 
                   selector:(SEL)aSelector 
                       name:(nullable NSString *)aName 
                     object:(nullable id)anObject;
    
        public func addObserver(observer: AnyObject, 
                      selector aSelector: Selector, 
                              name aName: String?, 
                         object anObject: AnyObject?)
    
        參數說明:
            observer :觀察者,即誰要接收這個通知;
            aSelector:收到通知後調用何種方法,即回調函數,而且把通知對象當作參數傳入;
            aName     :通知的名字,也是通知的惟一標示,編譯器就經過這個找到通知的。
                       爲 nil 時,表示註冊全部通知,那麼不管通知的名稱是什麼,監聽器都能收到這個通知;
            anObject :通知發送者,爲 nil 時,表示監聽全部發送者的通知。若是 anObject 和 aName 都爲 nil,監聽器都收到全部的通知。
    
        - (id)addObserverForName:(NSString *)name 
                          object:(id)obj 
                           queue:(NSOperationQueue *)queue 
                      usingBlock:(void (^)(NSNotification *note))block;
    
        參數說明:
            name :通知的名稱
            obj  :通知發佈者
            queue:決定了 block 在哪一個操做隊列中執行,若是傳 nil,默認在當前操做隊列中同步執行
            block:收到對應的通知時,會回調這個 block
    • Objective-C

      [[NSNotificationCenter defaultCenter] addObserver:self 
                                                   selector:@selector(notification1Sel) 
                                                       name:@"notification1" 
                                                     object:nil];
      
          [[NSNotificationCenter defaultCenter] addObserver:self 
                                                   selector:@selector(notification2Sel:) 
                                                       name:@"notification2" 
                                                     object:nil];
      
          // 通知觸發方法,通知無內容
          - (void)notification1Sel {
      
          }
      
          // 通知觸發方法,通知有內容
          - (void)notification2Sel:(NSNotification *)notification {
      
              // 接收用戶消息內容
              NSDictionary *userInfo = notification.userInfo;
          }
    • Swift

      NSNotificationCenter.defaultCenter().addObserver(self, 
                                                  selector: #selector(ViewController.notification1Sel), 
                                                      name: "notification1", 
                                                    object: nil)
      
          NSNotificationCenter.defaultCenter().addObserver(self, 
                                                  selector: #selector(ViewController.notification2Sel(_:)), 
                                                      name: "notification2", 
                                                    object: nil)
      
          // 通知觸發方法,通知無內容
          func notification1Sel() {
      
          }
      
          // 通知觸發方法,通知有內容
          func notification2Sel(notification:NSNotification) {
      
              // 接收用戶消息內容
              let userInfo = notification.userInfo
          }
  • 移除通知(觀察者)

    - (void)removeObserver:(id)observer;
        - (void)removeObserver:(id)observer name:(nullable NSString *)aName object:(nullable id)anObject;
    
        public func removeObserver(observer: AnyObject)
        public func removeObserver(observer: AnyObject, name aName: String?, object anObject: AnyObject?)
    
        參數說明:
            observer:觀察者,即在什麼地方接收通知;
            aName   :通知的名字,也是通知的惟一標示,編譯器就經過這個找到通知的。
            anObject:通知發送者,爲 nil 時,表示移除知足條件的全部發送者的通知。
    • Objective-C

      // 移除此觀察者的全部通知
          [[NSNotificationCenter defaultCenter] removeObserver:self];
      
          // 移除指定名字的通知
          [[NSNotificationCenter defaultCenter] removeObserver:self name:@"notification1" object:nil];
    • Swift

      // 移除此觀察者的全部通知
          NSNotificationCenter.defaultCenter().removeObserver(self)
      
          // 移除指定名字的通知
          NSNotificationCenter.defaultCenter().removeObserver(self, name:"notification1", object:nil)

四、異步發送 Notification 的使用

  • 在子線程中使用 [NSNotificationQueue defaultQueue] 將通知加入到通知隊列中,觀察者選擇器方法就會在子線程中被執行。

    | ->  ->  ->  ->  ->  ->  ->  -> main runloop ->  ->  ->  ->  ->  ->  ->  ->  ->  ->   |
        執行順序:main runloop -> |                                                                                      | -> main runloop
                                 | -> 發送通知 -> 觀察者選擇器方法(按照觀察者註冊的順序同步執行)-> 通知發送者方法中其它的操做 |
  • 發送異步通知

    - (void)enqueueNotification:(NSNotification *)notification 
                       postingStyle:(NSPostingStyle)postingStyle;
    
        - (void)enqueueNotification:(NSNotification *)notification 
                       postingStyle:(NSPostingStyle)postingStyle 
                       coalesceMask:(NSNotificationCoalescing)coalesceMask 
                           forModes:(nullable NSArray<NSString *> *)modes;
    
        參數說明:
            notification:通知
            postingStyle:發佈方式
            coalesceMask:合併方式
            modes       :運行循環模式,nil 表示 NSDefaultRunLoopMode
    
            NSPostingStyle                              :發佈方式
    
                NSPostWhenIdle = 1,                     :空閒時發佈
                NSPostASAP = 2,                         :儘快發佈
                NSPostNow = 3                           :當即發佈
    
            NSNotificationCoalescing                    :合併方式
    
                NSNotificationNoCoalescing = 0,         :不合並
                NSNotificationCoalescingOnName = 1,     :按名稱合併
                NSNotificationCoalescingOnSender = 2    :按發佈者合併
    • Objective-C

      // 建立通知
          NSNotification *asyncNotification = [NSNotification notificationWithName:@"asyncNotification" object:self];
      
          dispatch_async(dispatch_get_global_queue(0, 0), ^{
      
              // 將通知添加到發送隊列中,發送通知
              [[NSNotificationQueue defaultQueue] enqueueNotification:asyncNotification postingStyle:NSPostWhenIdle];
          });
  • 移除異步通知

    - (void)dequeueNotificationsMatching:(NSNotification *)notification coalesceMask:(NSUInteger)coalesceMask;
    
        參數說明:
            notification:通知
            coalesceMask:合併方式
    • Objective-C

      // 移除通知,不是當即發佈的通知能夠被移除
          [[NSNotificationQueue defaultQueue] dequeueNotificationsMatching:asyncNotification coalesceMask:0];

五、系統通知的使用

5.1 UIDevice 通知

  • UIDevice 類提供了一個單例對象,它表明着設備,經過它能夠得到一些設備相關的信息,好比電池電量值(batteryLevel)、電池狀態(batteryState)、設備的類型(model,好比 iPod、iPhone 等)、設備的系統(systemVersion)。經過 [UIDevice currentDevice] 能夠獲取這個單例對象。

  • UIDevice 對象會不間斷地發佈一些通知,下列是 UIDevice 對象所發佈通知的名稱常量:

    UIDeviceOrientationDidChangeNotification     // 設備旋轉
        UIDeviceBatteryStateDidChangeNotification    // 電池狀態改變
        UIDeviceBatteryLevelDidChangeNotification    // 電池電量改變
        UIDeviceProximityStateDidChangeNotification  // 近距離傳感器(好比設備貼近了使用者的臉部)

5.2 鍵盤通知

  • 咱們常常須要在鍵盤彈出或者隱藏的時候作一些特定的操做,所以須要監聽鍵盤的狀態。

  • 鍵盤狀態改變的時候,系統會發出一些特定的通知:

    UIKeyboardWillShowNotification         // 鍵盤即將顯示
        UIKeyboardDidShowNotification          // 鍵盤顯示完畢
        UIKeyboardWillHideNotification         // 鍵盤即將隱藏
        UIKeyboardDidHideNotification          // 鍵盤隱藏完畢
        UIKeyboardWillChangeFrameNotification  // 鍵盤的位置尺寸即將發生改變
        UIKeyboardDidChangeFrameNotification   // 鍵盤的位置尺寸改變完畢
  • 系統發出鍵盤通知時,會附帶一下跟鍵盤有關的額外信息(字典),字典常見的 key 以下:

    UIKeyboardFrameBeginUserInfoKey         // 鍵盤剛開始的 frame
        UIKeyboardFrameEndUserInfoKey           // 鍵盤最終的 frame(動畫執行完畢後)
        UIKeyboardAnimationDurationUserInfoKey  // 鍵盤動畫的時間
        UIKeyboardAnimationCurveUserInfoKey     // 鍵盤動畫的執行節奏(快慢)
相關文章
相關標籤/搜索