iOS - Notification 通知


1、Notification

  • 通知中心實際上是在程序內部提供了消息廣播的一種機制,它允許我們在低程度耦合的情況下,滿足控制器與一個任意的對象進行通信的目的。每一個 iOS 程序(即每一個進程)都有一個自己的通知中心,即 NSNotificationCenter 對象,該對象采用單例設計模式,可以通過類方法 defaultCenter 獲得當前進程唯一的通知中心對象。一個 NSNotificationCenter 可以有許多的通知消息 NSNotification,對於每一個 NSNotification 可以有很多的觀察者 Observer 來接收通知。NSNotificationCenter 是 iOS 中通知中心的靈魂,由該類實現了觀察者模式,並給開發者提供了諸如注冊、刪除觀察者的接口。

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

  • 任何一個對象都可以向通知中心發布通知(NSNotification),描述自己在做什么。其他感興趣的對象(Observer)可以申請在某個特定通知發布時(或在某個特定的對象發布通知時)收到這個通知。

    Notification1

  • 使用 [NSNotificationCenter defaultCenter] 發送的通知無論是在主線程還是子線程中被注冊,觀察者注冊的選擇器方法都會在主線程中被執行。執行順序為:main runloop -> 發送通知 -> 觀察者選 擇器方法(按照觀察者注冊的順序執行)-> 通知發送者方法中其它的操作 -> main runloop。

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

  • 如果要在同一台機器上進行進程間的通信,需要使用 NSDistributedNOtificationCenter。

  • 優勢:

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

    • 1、在編譯期不會檢查通知是否能夠被觀察者正確的處理;
    • 2、在釋放注冊的對象時,需要在通知中心取消注冊;
    • 3、在調試的時候應用的工作以及控制過程難跟蹤;
    • 4、需要第三方來管理 controller 與觀察者對象之間的聯系;
    • 5、controller 和觀察者需要提前知道通知名稱、UserInfo dictionary keys。如果這些沒有在工作區間定義,那么會出現不同步的情況;
    • 6、通知發出后,controller 不能從觀察者獲得任何的反饋信息。
  • 目的:

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

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

    • 共同點:

      • 利用通知和代理都能完成對象之間的通信(比如 A 對象告訴 D 對象發生了什么事情, A 對象傳遞數據給 D 對象)
    • 不同點:

      • 代理 : 1 個對象只能告訴另 1 個對象發生了什么事情。
      • 通知 :
        • 1 個對象能告訴 N 個對象發生了什么事情, 1 個對象能得知 N 個對象發生了什么事情。
        • 任何數量的對象都可以接收同一個消息,而不僅限定於委托對象。
        • 通知系統不接受返回值。
        • 對象不需要預先定義協議方法,就可以接收來自通知中心的消息。
        • 發布通知的對象只負責發布通知,不需要關心觀察者是否存在。
  • 通知中心是同步的,還是異步的 ?

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

2、系統發送 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)
      

3、自定義發送 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)
      

4、異步發送 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、系統通知的使用

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     // 鍵盤動畫的執行節奏(快慢)
    


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM