iOS NSNotification基本用法,本地和遠(yuǎn)程通知

Notification Center的概念:

它是一個(gè)單例對(duì)象,允許當(dāng)事件發(fā)生時(shí)通知一些對(duì)象,讓對(duì)象做出相應(yīng)反應(yīng)。

它允許我們?cè)诘统潭锐詈系那闆r下,滿足控制器與一個(gè)任意的對(duì)象進(jìn)行通信的目的。 這種模式的基本特征是為了讓其他的對(duì)象能夠接收到某種事件傳遞過(guò)來(lái)的通知,主要使用通知名稱來(lái)發(fā)送和接收通知。

基本上不用考慮其它影響因素,只需要使用同樣的通知名稱,監(jiān)聽(tīng)該通知的對(duì)象(即觀察者)再對(duì)通知做出反應(yīng)即可。

優(yōu)勢(shì):

1.不需要編寫(xiě)多少代碼,實(shí)現(xiàn)比較簡(jiǎn)單;

2.對(duì)于一個(gè)發(fā)出的通知,多個(gè)對(duì)象能夠做出反應(yīng),簡(jiǎn)單實(shí)現(xiàn)1對(duì)多的方式,較之于 Delegate 可以實(shí)現(xiàn)更大的跨度的通信機(jī)制;

3.能夠傳遞參數(shù)(object和userInfo),object和userInfo可以攜帶發(fā)送通知時(shí)傳遞的信息。

缺點(diǎn):

1.在編譯期間不會(huì)檢查通知是否能夠被觀察者正確的處理;

2.在釋放通知的觀察者時(shí),需要在通知中心移除觀察者;

3.在調(diào)試的時(shí)候,通知傳遞的過(guò)程很難控制和跟蹤;

4.發(fā)送通知和接收通知時(shí)需要提前知道通知名稱,如果通知名稱不一致,會(huì)出現(xiàn)不同步的情況;

5.通知發(fā)出后,不能從觀察者獲得任何的反饋信息。

<一>NSNotification 一般使用情況的的5種使用方式

本文demo1

1、不傳遞參數(shù), 最常用的一種

/*----------------------- 一 --------- 不傳遞參數(shù), 最常用的一種 ------------------------------------*/

// 第一步:監(jiān)聽(tīng)通知
- (void)ListeningToNotification1{
    //監(jiān)聽(tīng)
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(noti1) name:@"noti1" object:nil];
}

// 第二部:發(fā)送通知
-(void)btn1Click{
    [[NSNotificationCenter defaultCenter] postNotificationName:@"noti1" object:nil];
}

//調(diào)用方法
-(void)noti1{
    NSLog(@"接收 不帶參數(shù)的消息");
}

2、 使用監(jiān)聽(tīng)一方的調(diào)用方法時(shí) 傳遞消息

//發(fā)通知

/*--------------- 二 ------ 使用監(jiān)聽(tīng)一方的調(diào)用方法時(shí) 傳遞消息-------------*/

// 第一步:監(jiān)聽(tīng)通知
- (void)ListeningToNotification2{
    
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(noti2:) name:@"noti2" object:nil];
}

// 第二部:發(fā)送通知(可以跨控制器進(jìn)行發(fā)送)

-(void)btn2Click:(UIButton *)btn
{
    [[NSNotificationCenter defaultCenter] postNotificationName:@"noti2" object:[NSString stringWithFormat:@"%@",btn.titleLabel.text]];
}

//調(diào)用方法
-(void)noti2:(NSNotification *)noti
{
    //使用object處理消息
    NSString *info = [noti object];
    NSLog(@"接收 object傳遞的消息:%@",info);
}

3、使用發(fā)送通知一方的 userInfo 傳遞消息

/*----------- 三 ----------- 使用發(fā)送通知一方的userInfo 傳遞消息  ----------*/

// 第一步:監(jiān)聽(tīng)通知
-(void)ListeningToNotification3{
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(noti3:) name:@"noti3" object:nil];
}

//第二步:發(fā)送通知

-(void)btn3Click{
    NSDictionary *dic = [NSDictionary dictionaryWithObject:@"wangLei在這里" forKey:@"param"];
    // 我把方法的object都設(shè)為了nil。那么這個(gè)參數(shù)起到什么作用呢?這個(gè)參數(shù)有點(diǎn)像二次確認(rèn)的意思,就是在同一個(gè)通知name的情況下還可以通過(guò)object再次進(jìn)行細(xì)分通知。就拿上面這個(gè)小demo說(shuō),如果object為空,接收方會(huì)接受所有名字為giveName的通知。但是如果object不為空,接收方就會(huì)只接收名字為giveName的而且object正確的通知
    
    [[NSNotificationCenter defaultCenter] postNotificationName:@"noti3" object:nil userInfo:dic];
}


//調(diào)用方法
-(void)noti3:(NSNotification *)noti
{
    //使用userInfo處理消息
    NSDictionary  *dic = [noti userInfo];
    NSString *info = [dic objectForKey:@"param"];
    NSLog(@"接收 userInfo傳遞的消息:%@",info);
}

// 最后一步:移除通知
-(void)dealloc
{
    //移除觀察者,Observer不能為nil
    [[NSNotificationCenter defaultCenter] removeObserver:self];
}

4 :使用block接收通知,不用再指定方法了


//第一步:接收通知
- (void)ListeningToNotification4{
[[NSNotificationCenter defaultCenter]addObserverForName:@"noti4" object:nil queue:nil usingBlock:^(NSNotification *not){
    //使用userInfo處理消息
    NSDictionary  *dic = [not userInfo];
    NSString *info = [dic objectForKey:@"param"];
    NSLog(@"使用block接收傳遞的消息:%@",info);
}];
}

//第二步:發(fā)送通知
-(void)btn4Click{
    NSDictionary *dic = [NSDictionary dictionaryWithObject:@"wangLei" forKey:@"param"];
    // 我把方法的object都設(shè)為了nil。那么這個(gè)參數(shù)起到什么作用呢?這個(gè)參數(shù)有點(diǎn)像二次確認(rèn)的意思,就是在同一個(gè)通知name的情況下還可以通過(guò)object再次進(jìn)行細(xì)分通知。就拿上面這個(gè)小demo說(shuō),如果object為空,接收方會(huì)接受所有名字為giveName的通知。但是如果object不為空,接收方就會(huì)只接收名字為giveName的而且object正確的通知
    [[NSNotificationCenter defaultCenter] postNotificationName:@"noti4" object:nil userInfo:dic];
}

5、使用NSNotificationQueue實(shí)現(xiàn)通知的異步

// 第一步:注冊(cè)通知

- (void)ListeningToNotification5{
    /**
     *  注冊(cè)一個(gè)通知
     *
     */
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(getNotification:) name:@"notificationMethon" object:nil];
    
}

//第二部: 發(fā)出通知

- (void)btn5Click{
    
//    [[NSNotificationCenter defaultCenter] postNotificationName:@"notificationMethon" object:nil userInfo:@{@"key":@"value"}];
    
    NSNotification *notifacation = [[NSNotification alloc]initWithName:@"notificationMethon" object:nil userInfo:@{@"key":@"value1"}];
    [[NSNotificationQueue defaultQueue] enqueueNotification:notifacation postingStyle:NSPostNow];
/*

我們可以通過(guò),NSNotificationQueue的defaultQueue來(lái)獲取到這個(gè)通知隊(duì)列,然后調(diào)用enqueueNotification來(lái)發(fā)出通知,我們可以看到第二個(gè)參數(shù)postingStyle,這個(gè)參數(shù)是一個(gè)枚舉,他可以是以下三個(gè)值:

typedef NS_ENUM(NSUInteger, NSPostingStyle) {
    NSPostWhenIdle = 1,
    NSPostASAP = 2,
    NSPostNow = 3
};
這三個(gè)不同的值是有一定區(qū)別的。(以下內(nèi)容摘抄自網(wǎng)絡(luò))


盡快發(fā)送
以NSPostASAP風(fēng)格進(jìn)入隊(duì)列的通告會(huì)在運(yùn)行循環(huán)的當(dāng)前迭代完成時(shí)被發(fā)送給通
告中心,如果當(dāng)前運(yùn)行循環(huán)模式和請(qǐng)求的模式相匹配的話(如果請(qǐng)求的模式和當(dāng)
前模式不同,則通告在進(jìn)入請(qǐng)求的模式時(shí)被發(fā)出)。由于運(yùn)行循環(huán)在每個(gè)迭代過(guò)
程中可能進(jìn)行多個(gè)調(diào)用分支(callout),所以在當(dāng)前調(diào)用分支退出及控制權(quán)返回
運(yùn)行循環(huán)時(shí),通告可能被分發(fā),也可能不被分發(fā)。其它的調(diào)用分支可能先發(fā)生
,比如定時(shí)器或由其它源觸發(fā)了事件,或者其它異步的通告被分發(fā)了。


您通??梢詫SPostASAP風(fēng)格用于開(kāi)銷(xiāo)昂貴的資源,比如顯示服務(wù)器。如果在
運(yùn)行循環(huán)的一個(gè)調(diào)用分支過(guò)程中有很多客戶代碼在窗口緩沖區(qū)中進(jìn)行描畫(huà),在每
次描畫(huà)之后將緩沖區(qū)的內(nèi)容刷新到顯示服務(wù)器的開(kāi)銷(xiāo)是很昂貴的。在這種情況
下,每個(gè)draw...方法都會(huì)將諸如“FlushTheServer” 這樣的通告排入隊(duì)列,并指定
按名稱和對(duì)象進(jìn)行聚結(jié),以及使用NSPostASAP風(fēng)格。結(jié)果,在運(yùn)行循環(huán)的最
后,那些通告中只有一個(gè)被派發(fā),而窗口緩沖區(qū)也只被刷新一次。


空閑時(shí)發(fā)送

以NSPostWhenIdle風(fēng)格進(jìn)入隊(duì)列的通告只在運(yùn)行循環(huán)處于等待狀態(tài)時(shí)才被發(fā)
出。在這種狀態(tài)下,運(yùn)行循環(huán)的輸入通道中沒(méi)有任何事件,包括定時(shí)器和異步事
件。以NSPostWhenIdle風(fēng)格進(jìn)入隊(duì)列的一個(gè)典型的例子是當(dāng)用戶鍵入文本、而
程序的其它地方需要顯示文本字節(jié)長(zhǎng)度的時(shí)候。在用戶輸入每一個(gè)字符后都對(duì)文
本輸入框的尺寸進(jìn)行更新的開(kāi)銷(xiāo)是很大的(而且不是特別有用),特別是當(dāng)用戶
快速輸入的時(shí)候。在這種情況下,Cocoa會(huì)在每個(gè)字符鍵入之后,將諸
如“ChangeTheDisplayedSize”這樣的通告進(jìn)行排隊(duì),同時(shí)把聚結(jié)開(kāi)關(guān)打開(kāi),并使
用NSPostWhenIdle風(fēng)格。當(dāng)用戶停止輸入的時(shí)候,隊(duì)列中只有一
個(gè)“ChangeTheDisplayedSize”通告(由于聚結(jié)的原因)會(huì)在運(yùn)行循環(huán)進(jìn)入等待狀
態(tài)時(shí)被發(fā)出,顯示部分也因此被刷新。請(qǐng)注意,運(yùn)行循環(huán)即將退出(當(dāng)所有的輸
入通道都過(guò)時(shí)的時(shí)候,會(huì)發(fā)生這種情況)時(shí)并不處于等待狀態(tài),因此也不會(huì)發(fā)出
通告。

立即發(fā)送

以NSPostNow風(fēng)格進(jìn)入隊(duì)列的通告會(huì)在聚結(jié)之后,立即發(fā)送到通告中心。您可以
在不需要異步調(diào)用行為的時(shí)候 使用NSPostNow風(fēng)格(或者通過(guò)
NSNotificationCenter的postNotification:方法來(lái)發(fā)送)。在很多編程環(huán)境下,我
們不僅允許同步的行為,而且希望使用這種行為:即您希望通告中心在通告派發(fā)
之后返回,以便確定觀察者對(duì)象收到通告并進(jìn)行了處理。當(dāng)然,當(dāng)您希望通過(guò)聚
結(jié)移除隊(duì)列中類(lèi)似的通告時(shí),應(yīng)該用enqueueNotification...方法,且使用
NSPostNow風(fēng)格,而不是使用postNotification:方法。
*/
}

//
-(void)getNotification:(NSNotification *)info{
    NSDictionary *dict = info.userInfo;
    NSLog(@"當(dāng)前執(zhí)行任務(wù)的線程為%@",[NSThread currentThread]);
}
 

// 第三部:我們將調(diào)用發(fā)出通知的代碼放在一個(gè)非主隊(duì)列里面


    
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    [queue addOperationWithBlock:^{
        [self btn5Click];
        [NSThread sleepForTimeInterval:3];
        NSLog(@"1目前的queue所在的線程為%@",[NSThread currentThread]);
    }];

運(yùn)行結(jié)果如下:

2017-02-20 16:14:53.217 NSNotificationDemo[6438:492325] 這是通知后面的代碼,按照通知是同步的道理,應(yīng)該在通知完成回調(diào)之后才會(huì)執(zhí)行
2017-02-20 16:14:53.219 NSNotificationDemo[6438:493599] 這是通知里面執(zhí)行的代碼,這里的代碼正常情況下要執(zhí)行完之后,才能執(zhí)行通知之后的代碼;當(dāng)前執(zhí)行任務(wù)的線程為<NSThread: 0x600000077880>{number = 3, name = (null)}
2017-02-20 16:14:56.222 NSNotificationDemo[6438:493599] 1目前的queue所在的線程為<NSThread: 0x600000077880>{number = 3, name = (null)}

記住下面的話:

上面說(shuō)到 NSNotificationCenter是一個(gè)同步操作,也就是只有當(dāng)響應(yīng)的通知的代碼執(zhí)行完畢以后,發(fā)出通知的對(duì)象的代碼才會(huì)繼續(xù)往下執(zhí)行。
那么 NSNotificationQueue就有一些區(qū)別,他有兩個(gè)非常重要的特點(diǎn):即通告的聚結(jié)和異步發(fā)送。聚結(jié)是把和剛進(jìn)入隊(duì)列的通告相類(lèi)似的其它通告從隊(duì)列中移除的過(guò)程。如果一個(gè)新的通告和已經(jīng)在隊(duì)列中的通告相類(lèi)似,則新的通告不進(jìn)入隊(duì)列,而所有類(lèi)似的通告(除了隊(duì)列中的第一個(gè)通告以外)都被移除。然而,您不應(yīng)該依賴于這個(gè)特殊的聚結(jié)行為。
而異步發(fā)送則很好理解了,也就是說(shuō)發(fā)出通知以后立刻返回,也就是是繼續(xù)執(zhí)行下面的代碼,并不管通知發(fā)出后的具體情況

總結(jié)NSNotificationCenter和NSNotificationQueue的區(qū)別,也許最大的一點(diǎn)就是發(fā)出通知時(shí)一個(gè)是同步一個(gè)是異步。

最后,記得在發(fā)送通知消息的頁(yè)面,在dealloc方法里面移除觀察者。

-(void)dealloc

{

//移除觀察者,Observer不能為nil

[[NSNotificationCenter defaultCenter] removeObserver:self];

}

<二>NSNotification 添加本地通知的使用方式

demo2下載地址
<1>、本地通知欄通知
1.1、本地通知的創(chuàng)建和使用
本地通知其實(shí)在之前的文章和網(wǎng)上就已經(jīng)很詳細(xì)的說(shuō)了,主要是UILocalNotification的使用,本地通知主要應(yīng)用在固定時(shí)間的通知事件,比如日歷、活動(dòng)提醒等

    NSLog(@"添加本地通知");
    //ios8.0以上的系統(tǒng)需要注冊(cè)通知
    if (D_ISHight(8.0)) {
 //注冊(cè)通知
  [[UIApplication sharedApplication] registerUserNotificationSettings:[UIUserNotificationSettings settingsForTypes:UIUserNotificationTypeAlert|UIUserNotificationTypeSound|UIUserNotificationTypeBadge categories:nil]]; 
    }
    
    UILocalNotification *localNotification = [[UILocalNotification alloc] init];
    //設(shè)置固定時(shí)間通知
    if (sender.tag == LOCALNOTIFICATION1) {
        NSDateFormatter * forma = [[NSDateFormatter alloc] init];
        [forma setDateFormat:@"HH:mm"];
        localNotification.fireDate = [forma dateFromString:@"13:20"];   //12點(diǎn)提醒
        [localNotification setRepeatInterval:NSCalendarUnitDay];        //每天12點(diǎn)
    }
    //設(shè)置重復(fù)間隔時(shí)間
//    else if (sender.tag == LOCALNOTIFICATION1){
//        NSDate *date =[[NSDate alloc] init];
//        localNotification.fireDate = [date dateByAddingTimeInterval:5];//重復(fù)間隔的時(shí)長(zhǎng)
//        [localNotification setRepeatInterval:NSCalendarUnitSecond];//時(shí)間間隔單位,秒
//    }
    
    [localNotification setTimeZone:[NSTimeZone defaultTimeZone]];   //時(shí)區(qū)
    //ios8.2以上的系統(tǒng)可以設(shè)置標(biāo)題
    if (D_ISHight(8.2)) {
        [localNotification setAlertTitle:@"提醒標(biāo)題"];
    }
    [localNotification setAlertBody:@"提醒內(nèi)容:Damon"];
    [localNotification setAlertAction:@"鎖屏?xí)r顯示的動(dòng)作標(biāo)題"]; //在鎖屏?xí)r顯示的動(dòng)作標(biāo)題(完整測(cè)標(biāo)題:"滑動(dòng)來(lái)" + alertAction)
    [localNotification setApplicationIconBadgeNumber:1];    //設(shè)置提醒的軟件右上角的小紅點(diǎn)
    [localNotification setSoundName:UILocalNotificationDefaultSoundName];//默認(rèn)聲音
    //或者指定文件名localNotification.soundName = @"123.wav";
    [[UIApplication sharedApplication] scheduleLocalNotification:localNotification];    //調(diào)用通知

在本地消息調(diào)用之后,就會(huì)在通知欄顯示了,點(diǎn)擊通知欄的消息之后,會(huì)自動(dòng)打開(kāi)這個(gè)軟件,需要注意的是軟件不同的狀態(tài),點(diǎn)擊通知欄調(diào)用的函數(shù)也不同。

1、軟件如果是在后臺(tái)運(yùn)行,并沒(méi)有退出,調(diào)用的是AppDelegate的這個(gè)函數(shù)

- (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification

2、軟件已經(jīng)主動(dòng)退出,或者后臺(tái)超過(guò)時(shí)間退出,點(diǎn)擊通知欄消息之后就只會(huì)調(diào)用這個(gè)函數(shù)

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions

3、所以不同的狀態(tài),調(diào)用的函數(shù)也不同,那么想獲取是哪個(gè)通知調(diào)用的函數(shù)方法也不相同了,如果是后臺(tái)運(yùn)行狀態(tài)下,想知道某個(gè)通知,可以通過(guò)不同的userInfo這樣實(shí)現(xiàn)

- (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification
{
    NSLog(@"Application did receive local notifications");
    // 在這里寫(xiě)跳轉(zhuǎn)代碼
    // 如果是應(yīng)用程序在前臺(tái),依然會(huì)收到通知,但是收到通知之后不應(yīng)該跳轉(zhuǎn)
    if (application.applicationState == UIApplicationStateActive)
    {
        return;
    }
    if (application.applicationState == UIApplicationStateInactive) {
        // 當(dāng)應(yīng)用在后臺(tái)收到本地通知時(shí)執(zhí)行的跳轉(zhuǎn)代碼
        //可以通過(guò)設(shè)置通知時(shí)的userinfo過(guò)濾某條通知
        if ([notification.userInfo[@"name"] isEqualToString:@"Damon"]) {
            NSLog(@"damon");
        }
        //可以得到所有的通知
        for (UILocalNotification *noti in [[UIApplication sharedApplication] scheduledLocalNotifications]) {
            NSLog(@"%@",noti.fireDate);
        }
        //通知之后可以取消對(duì)應(yīng)的通知
        [[UIApplication sharedApplication] cancelLocalNotification:notification];
    }
    [self jump:1];
}

4、而如果app已經(jīng)退出,那么在啟動(dòng)函數(shù)里面獲取通知就需要通過(guò)launchOptions里面的UIApplicationLaunchOptionsLocalNotificationKey來(lái)實(shí)現(xiàn),比如這樣:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    //當(dāng)應(yīng)用退出之后,點(diǎn)擊通知跳轉(zhuǎn)到應(yīng)用會(huì)走這個(gè)函數(shù),而不是didReceiveLocalNotification
    UILocalNotification * local = [launchOptions objectForKey:UIApplicationLaunchOptionsLocalNotificationKey];
    if ([local.userInfo[@"name"] isEqualToString:@"Damon"]) {
        NSLog(@"damon");
        [self jump:2];
    }
    return YES;
}

轉(zhuǎn)自:http://www.hudongdong.com/ios/348.html

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

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

  • 應(yīng)用程序必須進(jìn)行適當(dāng)配置,才可以接受本地或遠(yuǎn)程通知。配置過(guò)程在iOS和OS X略有不同,但基本原理是相同的。在啟動(dòng)...
    shenzhenboy閱讀 1,472評(píng)論 1 2
  • 概述 在多數(shù)移動(dòng)應(yīng)用中任何時(shí)候都只能有一個(gè)應(yīng)用程序處于活躍狀態(tài),如果其他應(yīng)用此刻發(fā)生了一些用戶感興趣的那么通過(guò)通知...
    莫離_焱閱讀 6,717評(píng)論 1 8
  • 史上最全的iOS面試題及答案 iOS面試小貼士———————————————回答好下面的足夠了----------...
    Style_偉閱讀 2,575評(píng)論 0 35
  • 多線程、特別是NSOperation 和 GCD 的內(nèi)部原理。運(yùn)行時(shí)機(jī)制的原理和運(yùn)用場(chǎng)景。SDWebImage的原...
    LZM輪回閱讀 2,124評(píng)論 0 12
  • 一周很快,一周收獲了多少呢?會(huì)不會(huì)因?yàn)橐恢茏陨頋q價(jià)呢?最近一直都有堅(jiān)持在得到專(zhuān)欄聽(tīng)各種內(nèi)容,有關(guān)經(jīng)濟(jì)學(xué)的,有關(guān)時(shí)間...
    撒哈拉C閱讀 395評(píng)論 0 0

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