1. 程式人生 > >IOS 通知+非同步通知詳解

IOS 通知+非同步通知詳解

一、通知的基本使用

每一個應用程式都有一個通知中心,專門負責協助不同 物件之間的訊息通訊。

任何一個物件都可以向通知中心釋出通知,描述自己在做什麼。其他感興趣的物件可以申請在某個特定通知釋出時(或在某個特定的物件釋出通知時)收到這個通知。

//一個通知一般包含三個屬性:
@property (readonly, copy) NSString *name;//通知名稱
@property (nullable, readonly, retain) id object;//通知釋出者(是誰要釋出通知)
@property (nullable, readonly, copy) NSDictionary *userInfo;//一些額外的資訊(通知釋出者傳遞給通知接收者的資訊內容)
//初始化一個通知:(NSNotification)物件 + (instancetype)notificationWithName:(NSString *)aName object:(nullable id)anObject; + (instancetype)notificationWithName:(NSString *)aName object:(nullable id)anObject userInfo:(nullable NSDictionary *)aUserInfo; //釋出通知方法(通知中心提供了相應的方法來幫助釋出通知) //1.釋出一個notification通知,可在notification物件中設定通知的名稱,通知釋出者、額外的資訊等
- (void)postNotification:(NSNotification *)notification; //2.釋出一個名稱為aName的通知,anObject為這個通知的釋出者 - (void)postNotificationName:(NSString *)aName object:(nullable id)anObject; //3.釋出一個名稱為aName的通知,anObject為這個通知的釋出者,aUserInfo為額外資訊(通知的內容) - (void)postNotificationName:(NSString *)aName object:(nullable id)anObject userInfo:(nullable NSDictionary
*)aUserInfo;

在這裡舉個例子,比如這裡有新浪和騰訊新聞在釋出新聞。張三喜歡收聽娛樂新聞,李四喜歡收聽搞笑新聞。當新浪和騰訊發出的新聞只要是娛樂新聞張三都能收聽到,發出的新聞只要是搞笑新聞李四都能收聽到,怎麼實現呢,看如下程式碼:

//首先建立一個新聞釋出類,有一個屬性名字(比如新浪和騰訊)
#import <Foundation/Foundation.h>
@interface NewsCompany : NSObject
@property (nonatomic, copy) NSString *name;
@end

//再建立一個Person類(有一個name屬性和接收到新聞後做出的迴應)
@interface Person : NSObject
@property (nonatomic, copy) NSString *name;
//接收到訊息後觸發的方法
- (void)newCome:(NSNotification *)note;
@end

//Person.m檔案中實現接收到通知後的方法
#import "Person.h"
#import "NewsCompany.h"

@implementation Person

- (void)newCome:(NSNotification *)note{

    //取到通知釋出者
    NewsCompany *obj = note.object;
    NSLog(@"%@接收到%@的通知,通知內容是:%@",self.name,obj.name, note.userInfo);
}

//通知中心不會保留(retain)監聽器物件,在通知中心註冊過的物件,必須在該物件釋放前取消註冊。否則,當相應的通知再次出現時,通知中心仍然會向該監聽器傳送訊息。因為相應的監聽器物件已經被釋放了,所以可能會導致應用崩潰
- (void)dealloc {

    [[NSNotificationCenter defaultCenter] removeObserver:self];
}


//然後我們在ViewController.m檔案中實現他們

//首先建立兩個新聞釋出的物件(新浪和騰訊)
NewsCompany *company = [[NewsCompany alloc] init];
company.name = @"騰訊新聞";

NewsCompany *sian = [[NewsCompany alloc] init];
sian.name = @"新浪新聞";

//初始化兩個人
 Person *zhansan = [[Person alloc] init];
 zhansan.name = @"張三"; 

 Person *lisi = [[Person alloc] init];
 lisi.name = @"李四";

 //模擬釋出通知
 //初始化通知中心
 NSNotificationCenter *center = [NSNotificationCenter defaultCenter];

//註冊通知監聽器
    /**
     *  註冊一個監聽通知的監聽器
     *
     *  @param observer  監聽器,即誰要接收這個通知
     *  @param aSelector 收到通知後,回撥監聽器這個方法,並且把通知物件當做引數傳入
     *  @param aName     通知的名稱,如果為nil,那麼無論通知名稱是什麼,監聽器都能收到這個通知
     *  @param anObject  通知釋出者(如果為nil,不關心是誰釋出的通知,可以更具通知的名稱來獲得哪條通知,如果aName和anObject都為nil)
     */
// - (void)addObserver:(id)observer selector:(SEL)aSelector name:(nullable NSString *)aName object:(nullable id)anObject;

//註冊一個張三要接收軍事新聞的監聽器,接收到訊息後會呼叫- (void)newCome:(NSNotification *)note方法

[center addObserver:zhansan selector:@selector(newCome:) name:@"junshi_new_come" object:nil];

[center addObserver:lisi selector:@selector(newCome:) name:@"gaoxiao_new_come" object:nil];


//釋出通知
//騰訊釋出了一側通知
[center postNotificationName:@"junshi_new_come" object:company userInfo:@{@"title":@"哈哈哈哈哈我的武器好厲害",@"intro":@"哈哈哈哈哈我的武器好厲害......"}];

//新浪釋出了一條搞笑新聞
[center postNotificationName:@"gaoxiao_new_come" object:sian userInfo:@{@"title":@"哈哈哈太搞笑了"}];

//然後執行工程列印如下:
2016-09-12 22:08:07.552 通知的使用[1748:85334] 張三接收到騰訊新聞的通知,通知內容是:{
    intro = "\U4f0a\U62c9\U514b\U6218\U4e89\U505c\U6b62\U4e86......";
    title = "\U4f0a\U62c9\U514b\U6218\U4e89\U505c\U6b62\U4e86";
}
2016-09-12 22:08:07.552 通知的使用[1748:85334] 李四接收到新浪新聞的通知,通知內容是:{
    title = "\U54c8\U54c8\U54c8\U592a\U641e\U7b11\U4e86";
}

//證明張三和李四都接收到了各自喜愛的新聞

注意事項

  • 在通知中心註冊過的物件,必須在該物件釋放前取消註冊。否則,當相應的通知再次出現時,通知中心仍然會向該監聽器傳送訊息。因為相應的監聽器物件已經被釋放了,所以可能會導致應用崩潰
- (void)dealloc {

    [[NSNotificationCenter defaultCenter] removeObserver:self];
}
  • 執行順序:一定要先向通知中心註冊通知監聽器,也就是誰要監聽誰釋出的訊息,然後再執行釋出訊息,不然會導致訊息發出來了沒人接收的情況

通知的使用方法二:

使用Block的方法監聽通知:
- (id <NSObject>)addObserverForName:(nullableNSString *)name object:(nullableid)obj queue:(nullableNSOperationQueue *)queue usingBlock:(void (^)(NSNotification *note))block NS_AVAILABLE(10_6, 4_0);

@interface ViewController ()
@property (nonatomic, weak) id observe;
@end

- (void)test2 {//監聽通知方式二:       
//1.監聽通知
    /**
     *  監聽通知
     *
     *  Name:通知名稱
     object:誰發出的通知
     queue:佇列(決定block在哪個執行緒中區執行,傳入nil:在釋出通知的執行緒中去執行,也就是釋出通知在哪個執行緒,block就在哪個執行緒中區執行)
     usingBlock:監聽到通知的block回撥
     注意:一定要移除
     */

    _observe = [[NSNotificationCenter defaultCenter] addObserverForName:@"name" object:nil queue:nil usingBlock:^(NSNotification * _Nonnull note) {

        //只要監聽到通知 就會呼叫
        NSLog(@"%@",[NSThread currentThread]);


    }];


    //2.傳送通知
    /**
     *  Name:通知名字
     object:誰發出的通知
     */
    [[NSNotificationCenter defaultCenter] postNotificationName:@"note" object:nil];


    
}
//方式二:移除通知
- (void)dealloc {
        
    [[NSNotificationCenterdefaultCenter] removeObserver:_observe];
}

二、通知在非同步執行緒中的使用

非同步監聽通知方法一:

- (void)test3{
   
     //方法一:非同步監聽通知
    dispatch_async(dispatch_get_global_queue(0, 0), ^{

        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(reciveNote) name:@"note" object:nil];
    });
    
}

//防止先發送通知,後監聽通知,導致接收不到通知,這裡採用延遲傳送通知(點選屏幕後再發送通知)
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {

    //2.傳送通知
    /**
     *  Name:通知名字
     object:誰發出的通知
     */
    [[NSNotificationCenter defaultCenter] postNotificationName:@"note" object:nil];
}

/**
 *  監聽到通知就會呼叫
    非同步:監聽通知 主執行緒:發出通知
 //總結:接收通知程式碼,由釋出通知執行緒決定
 */
- (void)reciveNote {

    //注意點:如果是在非同步傳送通知,如果需要更新UI,為了安全起見,需要回到主執行緒更新UI
    dispatch_async(dispatch_get_main_queue(), ^{

        NSLog(@"%@",[NSThread currentThread]);
        //列印結果:2016-09-18 22:59:42.915 通知-多執行緒使用[1748:87249] <NSThread: 0x7fcff0c05b40>{number = 1, name = main}

    });
}

- (void)dealloc {

    //方式一:移除通知
  [[NSNotificationCenter defaultCenter] removeObserver:self];
非同步監聽通知方法二:
- (void)test4 {//非同步通知方式二:      
 //1.監聽通知

    //queue:[NSOperationQueue mainQueue]即使傳送通知在非同步,block也會在主執行緒中執行,所以為了安全起見queue:一般是使用主佇列
    _observe = [[NSNotificationCenter defaultCenter] addObserverForName:@"note" object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification * _Nonnull note) {

        //只要監聽到通知 就會呼叫
        NSLog(@"%@",[NSThread currentThread]);

    }];

    //2.傳送通知
    /**
     *  Name:通知名字
     object:誰發出的通知
     */

    dispatch_async(dispatch_get_global_queue(0, 0), ^{

         [[NSNotificationCenter defaultCenter] postNotificationName:@"note" object:nil];
    });
}

- (void)dealloc {
    //方式二:移除通知
    [[NSNotificationCenter defaultCenter] removeObserver:_observe];
}