iOS 趣談設計模式——通知

TereseXtk 8年前發布 | 13K 次閱讀 iOS開發 移動開發 IOS

【前言介紹】

iOS的一種設計模式,觀察者Observer模式(也叫發布/訂閱,即Publich/Subscribe模式)。
觀察者模式,包含了通知機制(notification)和KVO(Key-value-observing)機制。
在這本文中,我們將介紹在日常項目當中經常使用到的通知機制這一種設計模式。

通知機制
委托機制是代理“一對一”的對象之間的通信,而通知機制是廣播“一對多”的對象之間的通信;

一、是什么?【生活問題例子】

“短信天氣預報”

iOS 趣談設計模式——通知

 

當A類發送一條信息給通知中心時,注冊為用戶(觀察者)的B類群就會收到相應的通知,并作出反應。

二、有什么用?【代碼中的應用】

在不同類之間如何傳遞數據?
有幾種方法:屬性傳遞、代理協議,另外就是通知。
通知:在A類中創建的方法,B類中執行,且可以使用該通知攜帶數據傳遞給對方;

三、有什么不同?【與其他“通知”的不同?】

經常提到的通知,有“廣播通知”、“本地通知”、“推送通知”
本文所介紹的就是廣播通知,是實現觀察者模式的一種機制,可以在一個應用中的多個對象之間進行通信傳遞數據。
而本地通知和推送通知主要是給用戶發送“通知提示”,例如警告提示、聲音、震動以及如圖標上的紅色數字提示。
第一種由“本地發送通知”給用戶,第二種由第三方應用發送給蘋果官方的遠程服務器,然后再由服務器“推送通知”給用戶。

四、產品經理:老規矩,代碼拿來~【具體實現】

過程
在通知機制中,需要(或者說感興趣)接收某個通知的信息的所有對象都可以成為接收者,首先注冊成為觀察者。
進行注冊后,通知中心就會把發布者發送的通知信息,廣播給注冊過該通知的觀察者。且觀察者只能接收到通知中心的信息,不能知道通知是誰投送的。
最后,接受者不想再對關注該通知的信息時,可以給通知中心發生解除注冊的信息,之后都不再接收到通知了。

1.獲取通知中心(NSNotificationCenter)對象:(就像獲取移動營運商短信中心的權限,作為媒介)

發布、注冊、解除通知都需要使用通知中心,負責協助不同對象、不同類之間的消息通信。

[NSNotificationCenter  defaultCenter];  //需要注意的是,通知中心也是一個單例

2.發布(A類)和接收(B類)

a.做為發布者的A類發送通知
可以使用一下三個方法:

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

  • (void)postNotificationName:(NSString *)aNameobject:(id)anObject;
  • (void)postNotificationName:(NSString )aNameobject:(id)anObject userInfo: (NSDictionary )aUserInfo;</code></pre>
    • postNotificationName:指定消息名稱;
    • object:指定發消息者;
    • userInfo:通知中用于傳遞參數的載體,傳遞的方法是把參數放在NSDictionary類型的userInfo中。例如:NSDictionary *dict = [notification userInfo];

    一般使用第三個方法,如果參數不需要的,可以設置為nil.

    b.注冊通知,加入觀察者:
    做為觀察者B類注冊通知,進行監聽:

    - (void)addObserver:(id)observer selector:(SEL)aSelector name:(nullable NSString *)aName object:(nullable id)anObject;
    //@selector中為回調方法,在本類中對通知進行相應的處理,name為通知名稱、object為對象;

    剖析

    • object==nil,那么客戶對象(self)將收到任何對象發出NSWindowDidBecomeMainNotification的通知消息;
    • name==nil,那么觀察者將接收到object對象的所有消息,但是確定不了接收這些消息的順序。
    • object==nil,name==nil,那么該觀察者將收到所有對象的所有消息。

    對于一個任意的觀察者observer,如果不能保證其對應的selector有本類自定義的方法:(例如,MyMethod),可采用[observer respondsToSelector:@selector(MyMethod:)]]進行檢查。
    所以完整的添加觀察者過程為:

    if([observer respondsToSelector:@selector(MyMethod:)]) {
    [[NSNotificationCenter defaultCenter] addObserver:observer selector:
    @selector(MyMethod:) name:NSWindowDidBecomeMainNotification object:nil];
    }

    當然在蘋果API中也有另外一個注冊觀察者的方法:

    - (id )addObserverForName:(nullable NSString *)name object:(nullable id)obj queue:(nullable NSOperationQueue *)queue usingBlock:(void (^)(NSNotification *note))block

    此方法是支持在該方法中進行block回調的,而queue參數就是表示此模塊在queue隊列中進行。
    但是這方法一般不采用,所以建議使用第一種方法進行觀察者的創建。

    c.移除通知
    由于通知中心不會retain觀察者對象,因此注冊過的對象必須在釋放之前注銷掉,如果不這樣的話,當該通知再次出現時,通知中心會向已釋放的觀察者對象發送消息,從而導致應用崩潰。
    在ARC下,系統會自動回收無用的通知對象內存,但是由于系統回收機制ARC有一定的延遲性,所以即使不會出錯,也建議養成習慣,對通知進行手動釋放無用的通知。
    移除有2種方法:

    //釋放所有通知
  • (void)removeObserver:(id)observer;

//釋放名稱為aName的通知

  • (void)removeObserver:(id)observer name:(nullable NSString )aName object:(nullable id)anObject;</code></pre>

    一般在視圖控制器中,可以在“didReceiveMemoryWarning:”中發送解除消息:

    -(void)didReceiveMemoryWarning
    {
    [super didReceiveMemoryWarning];
    //移除觀察者
    [[NSNotificationCenter defaultCenter]removeObserver:self];
    }

    五、那些年我們用過的系統通知名稱~

    系統自帶的也有許多有用的通知,我們只需要注冊為相應的通知接收對象,就能根據通知狀態的變化發生相應的數據改變。
    部分系統通知名稱如下:

    UIApplicationDidFinishLaunchingNotification   // 應用程序啟動后
    UIApplicationDidBecomActiveNotification       //進入前臺
    UIApplicationWillResignActiveNotification     //應用將要進入后臺
    UIApplicationDidEnterBackgroundNotification   //進入后臺
    UIKeyboardWillShowNotification       // 鍵盤即將顯示
    UIKeyboardDidShowNotification        // 鍵盤顯示完畢
    UIKeyboardWillHideNotification       // 鍵盤即將隱藏
    UIKeyboardDidHideNotification        // 鍵盤隱藏完畢

    六、舉個栗子

    本文有2個例子:

    • 一個是完整的通知發布、接收、解除過程;
    • 一個是系統通知名稱的應用(以第三個:UIApplicationWillResignActiveNotification
      為例);

    (by:覺得文章太長不想看這段的童鞋,也可以到github上下載啊左的demo,:Mydemo1Mydemo2。自己琢磨琢磨。
    點擊“DownLoad ZIP”按鈕就可以了。一般使用Safari瀏覽器下載得了,啊左用QQ瀏覽器貌似下載不了...囧)

    【本次開發環境: Xcode:7.2 iOS Simulator:iphone6 By:啊左】

    1.完整的通知發布、接收、解除過程:

    UI控件擺放如下,視圖、控件的背景可以自己設置成比較明顯的顏色,便于觀察:

    iOS 趣談設計模式——通知

    iOS 趣談設計模式——通知

    • A視圖創建一個textView用于顯示B視圖傳遞過來的信息,一個按鈕用于切換到B視圖;
    • B視圖創建一個文本框用于更新信息,一個按鈕用于把文本框的信息更新并返回到視圖A。

    然后,點擊A類的按鈕,并且按住control拖拽到B視圖的控制器后松開鼠標,在彈出的選擇框(如下圖)選擇:“Present Modally”用于創建A、B控制器之間的模態類型的Segue。

    iOS 趣談設計模式——通知

    接下來,我們需要在新建一個視圖控制器B類SeocndViewController:

    iOS 趣談設計模式——通知

    回到故事板中,選擇B視圖控制器,打開標識檢查器(下圖第一排第三個選項),選擇class為:SeocndViewController。這就使代碼與故事板中的視圖控制器對應起來。(A視圖默認對應ViewController,如果有錯誤,可以檢查一下。)

    iOS 趣談設計模式——通知

    然后我們打開輔助編輯器,按住control,拖拽A視圖中的文本連接到對應的輸出口,這里我們命名為“myLabel”.

    iOS 趣談設計模式——通知

    以此方式,繼續為B類中的文本框連接到代碼中,并命名為:“MyTextView”,
    為B類的按鈕添加行為,方法名為:“saveBtn:”,
    啊左還是覺得上代碼實在點:
    (ViewController.h類)

    #import
    @interface ViewController : UIViewController
    //每次視圖打開后,監聽B類的數據是否發生變化,如有變化,在這個文本視圖中顯示更新。
    @property (weak, nonatomic) IBOutlet UITextView myLabel;
    @end</code></pre>

    (ViewController.m類)

    #import "ViewController.h"
    @interface ViewController ()
    @end
    @implementation ViewController
  • (void)viewDidLoad { [super viewDidLoad]; //1.注冊為觀察者,監聽B視圖中的通知 [[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(AMethod:) name:@"MyNotificationName" object:nil]; }

//回調方法:AMethod: -(void)AMethod:(NSNotification )notification { //2.獲取通知攜帶的數據,更新label的文本信息 NSDictionary dictData = [notification userInfo]; NSString *str = [dictData objectForKey:@"MyUserInfoKey"]; self.myLabel.text = str; }

  • (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; //3.移除所有通知 [[NSNotificationCenter defaultCenter]removeObserver:self]; } @end</code></pre>

    (SecondViewController.h)

    #import
    @interface SecondViewController : UIViewController
    //文本框,用于更新傳遞給ViewController視圖的數據
    @property (weak, nonatomic) IBOutlet UITextField *MyTextView; 
  • (IBAction)saveBtn:(UIButton *)sender; //保存返回按鈕事件 @end</code></pre>

    (SecondViewController.m)

    #import "SecondViewController.h"
    @interface SecondViewController ()
    @end
    @implementation SecondViewController

  • (void)viewDidLoad { [super viewDidLoad]; }

  • (IBAction)saveBtn:(UIButton *)sender {

//返回視圖A并在塊中發布通知 [self dismissViewControllerAnimated:YES completion:^{ //1.創建userInfo攜帶的信息 NSString str = self.MyTextView.text; NSDictionary dictData = [NSDictionary dictionaryWithObject:str forKey:@"MyUserInfoKey"];

//2.發布信息 [[NSNotificationCenter defaultCenter]postNotificationName:@"MyNotificationName" object:nil userInfo:dictData]; }];

} @end</code></pre>

驗證:
第一次A視圖的文本視圖中沒有顯示數據,點擊按鈕“確定切換頁面”,打開視圖B,在文本框中輸入信息(例如123),點擊“保存返回”按鈕,在A視圖的文本視圖中看到更新的信息:123。
by:有需要的童鞋可以到github上下載啊左的demo:Mydemo1

2.系統通知名稱的應用(以UIApplicationWillResignActiveNotification為例):

UIApplicationWillResignActiveNotification的意思是應用即將進入后臺的這個時刻。
首先,創建UI界面如下,相比第一個例子,這個會簡單很多:一個按鈕+一個顯示顏色的UIView視圖。

iOS 趣談設計模式——通知

創建一個命名為“myView”的UIView控件,一個方法為“changeColorBtn:”的按鈕行為即可,關聯ViewController控制器。
代碼如下:
(ViewController.h類)

#import
@interface ViewController : UIViewController
@property (weak, nonatomic) IBOutlet UIView *myView;

  • (IBAction)changeColorBtn:(UIButton *)sender; @end</code></pre>

    (ViewController.m類)

    #import "ViewController.h"
    @interface ViewController ()
    @end
    @implementation ViewController

  • (void)viewDidLoad {
    [super viewDidLoad];

//1. 注冊為觀察者
[[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(applicationWillResignActiveNotification:) name:UIApplicationWillResignActiveNotification object:nil]; }

//2.當應用即將進入后臺時,調用通知回調方法: -(void)applicationWillResignActiveNotification:(NSNotification *)notification{
//返回后臺的過程,把視圖背景改為紅色;
self.myView.backgroundColor = [UIColor redColor]; }

  • (IBAction)changeColorBtn:(UIButton *)sender {
    //按鈕把視圖背景改為黃色;
    self.myView.backgroundColor = [UIColor yellowColor]; } @end</code></pre>

    視圖第一次打開,視圖為默認白色:
    點擊按鈕,視圖變為黃色:

    iOS 趣談設計模式——通知

    >【按鈕事件】

    按住“command+shift”,雙擊H,進入iOS多任務欄;
    或者按住“command+shift”,單擊H,回到模擬器主界面。

    iOS 趣談設計模式——通知

    >【iOS多任務欄】

    發現,以上2種情況都可以看到視圖變為紅色。
    且回到應用后,顏色仍然是紅色。

    iOS 趣談設計模式——通知

    >【回到前臺】

    也就是,當應用從活躍的狀態進入非活躍狀態的時候,系統自動發送“UIApplicationWillResignActiveNotification”這個通知,如有注冊監聽者(觀察者),則執行回調方法。
    by:有需要的童鞋可以到github上下載啊左的demo:Mydemo2


    文/啊左(簡書)

 本文由用戶 TereseXtk 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!