iOS通知(Notification)使用の概要

9152 ワード

iOSの通知(Notification)は基本的にsync(同期)で送信されます.つまり、通知が送信された後、現在のスレッドがブロックされ、登録されているすべての通知者がこの通知を受信し、現在のスレッドで対応する処理が完了するまで、プログラムは実行されません.
この点をテストするために、BYNotifyPosterとBYNotifyReceiverの2つのクラスを作成し、それぞれ通知の送信と受信を担当することができます.テストプロセスでは,プライマリスレッドと非同期スレッドのそれぞれに通知を送信し,受信者が実行する状況をテストする.
BYNotifyReceiver
//
//  BYNotifyReceiver.h
//  NotificationTest
//
//  Created by Beryter on 2017/12/23.
//  Copyright © 2017  Beryter. All rights reserved.
//

#import 

@interface BYNotifyReceiver : NSObject

@end


#import "BYNotifyReceiver.h"

@implementation BYNotifyReceiver

- (instancetype)init
{
    if (self = [super init]) {
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleNotification:) name:@"BYNotificationTest" object:nil];
    }
    return self;
}

/*!
 * @brief    (BYNotificationTest)
 * @param   note NSNotification 
 */
- (void)handleNotification:(NSNotification *)note
{
    if ([[NSThread currentThread] isMainThread]) {
        NSLog(@"  - BYNotificationTest, , ");
    } else {
        NSLog(@"  - BYNotificationTest, , ");
    }
}

- (void)dealloc
{
    [[NSNotificationCenter defaultCenter] removeObserver:self name:@"BYNotificationTest" object:nil];
}

@end


BYNotifyPoster
//
//  BYNotifyPoster.h
//  NotificationTest
//
//  Created by Beryter on 2017/12/23.
//  Copyright © 2017  Beryter. All rights reserved.
//

#import 

@interface BYNotifyPoster : NSObject

- (void)postNotification;

@end

#import "BYNotifyPoster.h"

@implementation BYNotifyPoster

/*!
 * @brief  BYNotificationTest
 */
- (void)postNotification
{
    NSLog(@"  - BYNotificationTest");
    [[NSNotificationCenter defaultCenter] postNotificationName:@"BYNotificationTest" object:nil];
    NSLog(@"BYNotificationTest -  , ");
    // TODO: , 
}

@end


次のコードをテストViewControllerに追加します.
- (void)viewDidAppear:(BOOL)animated
{
    [super viewDidAppear:animated];
    
    NSLog(@"------------ ---------------");
    [self.poster postNotification];
    
    sleep(5);// 
    
    NSLog(@"------------ --------------");
     __weak typeof(self) weakSelf = self;
    dispatch_queue_t queue = dispatch_queue_create("NOTIFICATION_TEST_QUEUE", NULL);
    dispatch_async(queue, ^{
        [weakSelf.poster postNotification];
    });
}

実行後、テスト結果は次のとおりです.
2017-12-23 14:25:37.951231+0800 NotificationTest[4327:320244] ------------ ---------------
2017-12-23 14:25:37.951388+0800 NotificationTest[4327:320244]   - BYNotificationTest
2017-12-23 14:25:37.951496+0800 NotificationTest[4327:320244]   - BYNotificationTest, , 
2017-12-23 14:25:37.951604+0800 NotificationTest[4327:320244] BYNotificationTest -  , 
2017-12-23 14:25:42.952721+0800 NotificationTest[4327:320244] ------------ --------------
2017-12-23 14:25:42.953099+0800 NotificationTest[4327:320290]   - BYNotificationTest
2017-12-23 14:25:42.953337+0800 NotificationTest[4327:320290]   - BYNotificationTest, , 
2017-12-23 14:25:42.953516+0800 NotificationTest[4327:320290] BYNotificationTest -  , 


実行後に印刷されたlogログによると、BYNotifyPosterは通知を送信した後、通知されたすべてのオブジェクトが関連することを処理してから下へ実行し続けるのを待っており、確かにsync(同期)方式で行われています.BYNotifyReceiverが通知を受信して印刷したlogログを見ると,関連処理ロジックが存在するスレッドと通知を送信するスレッドが同じスレッドであることがわかる.一言で言えば、Notificationはsync(同期)方式で送信され、すなわち、送信通知が出た後に現在のスレッドが引っかかり、登録されたすべての通知者がこの通知を受信し、現在のスレッド(すなわち通知を発行したスレッド)で対応する処理が完了するまでプログラムは実行されない.
通知を登録する別の方法
引き続き下を見ると、登録通知のAPIでは、次のような興味深いAPIが発見されるかもしれません.
- (id )addObserverForName:(nullable NSNotificationName)name object:(nullable id)obj queue:(nullable NSOperationQueue *)queue usingBlock:(void (^)(NSNotification *note))block API_AVAILABLE(macos(10.6), ios(4.0), watchos(2.0), tvos(9.0));
    // The return value is retained by the system, and should be held onto by the caller in
    // order to remove the observer with removeObserver: later, to stop observation.

この方法は,登録観察者に通知する際にqueueとblockを加え,コードを簡略化し,同様にコードロジックをよりコンパクトにし,コードの可読性も高いことが分かる.observerを指定するのではなく、observerを返します.ここで、queueを指定すると、blockの実行は指定したqueueで実行され、queueが指定されていない場合、postメッセージスレッドでblockの実行が実行されます.ここでblockはループリファレンスを引き起こし、使用中にweakSelfを使用することに注意してください.キーコードは次のとおりです.
// BYNotifyReceiver 
- (instancetype)init
{
    if (self = [super init]) {
   
        NSOperationQueue *queue = [[NSOperationQueue alloc] init];
        queue.name = @"NOTIFICATION_HANDLE_QUEUE";
        __weak typeof(self) weakSelf = self;
       _notificationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:@"BYNotificationTest" object:nil queue:queue usingBlock:^(NSNotification * _Nonnull note) {
           [weakSelf testQueueNotification];
        }];
    }
    return self;
}

- (void)testQueueNotification
{
   [NSThread sleepForTimeInterval:5];// 5 
    if ([[NSThread currentThread] isMainThread]) {
        NSLog(@"  - BYNotificationTest, , ,currentQueue = %@",[NSOperationQueue currentQueue].name);
    } else {
        NSLog(@"  - BYNotificationTest, , ,currentQueue = %@",[NSOperationQueue currentQueue].name);
    }
    //  , notificationObserver, self
    [[NSNotificationCenter defaultCenter] removeObserver:self.notificationObserver];
}

//ViewController 
- (void)viewDidAppear:(BOOL)animated
{
    [super viewDidAppear:animated];
    NSLog(@"------------ ---------------");
    [self.poster postNotification];
    self.receiver = nil;
}


logログは以下の通りです.あまり説明しなくてもよく見てください.
2017-12-23 16:37:21.559728+0800 NotificationTest[7607:678009] ------------ ---------------
2017-12-23 16:37:21.559865+0800 NotificationTest[7607:678009]   - BYNotificationTest
2017-12-23 16:37:26.565126+0800 NotificationTest[7607:678048]   - BYNotificationTest, , ,currentQueue = NOTIFICATION_HANDLE_QUEUE
2017-12-23 16:37:26.565565+0800 NotificationTest[7607:678009] BYNotificationTest -  , 


非同期通知の実装
非同期通知をどのように実現しますか?本当にasync(非同期)で通知を送信するには、NSNotificationQueueを使用する必要があります.一般的に、これは複数の通知を統合して一緒に送信するために使用されるものであり、もちろんasync送信通知としても使用できる.その中にいくつかの重要な点があります.知っておく必要があります.
NSPostingStyleは、通知をいつ送信するかを決定するために使用されます.
typedef NS_ENUM(NSUInteger, NSPostingStyle) {
    NSPostWhenIdle = 1,  // runloop waiting post
    NSPostASAP = 2,      // runloop post
    NSPostNow = 3        // post, sync!
};

NSNotificationCoalescingは、複数の通知を合成するかどうかを決定し、どのように合成するかを決定します.
typedef NS_OPTIONS(NSUInteger, NSNotificationCoalescing) {
    NSNotificationNoCoalescing = 0,        // 
    NSNotificationCoalescingOnName = 1,    // name( ) 
    NSNotificationCoalescingOnSender = 2   // sender( ) 
};

runloop modeも設定できます.runloop modelが設定されている場合、通知は特定のrunloop modeでのみ送信されます.
関連コードは次のとおりです.
BYNotifyReceiver.m
- (instancetype)init
{
    if (self = [super init]) {
       [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleAsyncNotification:) name:@"BYAsyncNotificationTest" object:nil];
    }
    return self;
}

- (void)handleAsyncNotification:(NSNotification *)note
{
    if ([[NSThread currentThread] isMainThread]) {
        NSLog(@"  - BYAsyncNotificationTest, , ,currentQueue = %@",[NSOperationQueue currentQueue].name);
    } else {
        NSLog(@"  - BYAsyncNotificationTest, , ,currentQueue = %@",[NSOperationQueue currentQueue].name);
    }
}

BYNotifyPoster.m
/*!
 * @brief  
 */
- (void)asyncPostNotification
{
    NSLog(@"  - BYAsyncNotificationTest");
    NSNotification *asyncNotification = [NSNotification notificationWithName:@"BYAsyncNotificationTest" object:nil];
    [[NSNotificationQueue defaultQueue] enqueueNotification:asyncNotification postingStyle:NSPostASAP];
    NSLog(@"BYAsyncNotificationTest -  , ");
    // TODO: , 
    [self testAsync];
}

- (void)testAsync
{
    NSLog(@"%@", NSStringFromSelector(_cmd));
}


ViewController.m
- (void)viewDidAppear:(BOOL)animated
{
    [super viewDidAppear:animated];
    NSLog(@"------------ ---------------");
    [self.poster asyncPostNotification];
}

運転後、ログを印刷すると、答えが一目瞭然です.
2017-12-23 17:58:24.080311+0800 NotificationTest[9651:900465] ------------ ---------------
2017-12-23 17:58:24.080736+0800 NotificationTest[9651:900465]   - BYAsyncNotificationTest
2017-12-23 17:58:24.080926+0800 NotificationTest[9651:900465] BYAsyncNotificationTest -  , 
2017-12-23 17:58:24.081192+0800 NotificationTest[9651:900465] testAsync
2017-12-23 17:58:24.081644+0800 NotificationTest[9651:900465]   - BYAsyncNotificationTest, , ,currentQueue = NSOperationQueue Main Queue

どのように合併するかの通知については、ここではしばらく話さないで、テストコードはアップロードする必要はありません.遊びたい同志たちに残して遊びに行きます.通知にuserinfoパラメータが付いている場合、通知がマージされ、userinfoはどのように処理されるのか、マージされるのか、興味深い場所です.合併されたらどうなるのでしょうか.統合されなければ、受信者に通知されたuserinfoの中には何があるのでしょうか.
本文はオリジナルの文章で、転載は出典を明記してください.
グループと一緒に交流して共同学習することができます:801216530.