NSNotificationCenterの使用概要

8292 ワード

通知センターはiOS開発者にとって最もよく知られており、ページ間での転送を実現する一対多のメッセージングを実現しています.NSNotificationCenterの主な方法は次のとおりです.

@property (class, readonly, strong) NSNotificationCenter *defaultCenter;

- (void)addObserver:(id)observer selector:(SEL)aSelector name:(nullable NSNotificationName)aName object:(nullable id)anObject;

- (void)postNotification:(NSNotification *)notification;
- (void)postNotificationName:(NSNotificationName)aName object:(nullable id)anObject;
- (void)postNotificationName:(NSNotificationName)aName object:(nullable id)anObject userInfo:(nullable NSDictionary *)aUserInfo;

- (void)removeObserver:(id)observer;
- (void)removeObserver:(id)observer name:(nullable NSNotificationName)aName object:(nullable id)anObject;

- (id )addObserverForName:(nullable NSNotificationName)name object:(nullable id)obj queue:(nullable NSOperationQueue *)queue usingBlock:(void (^)(NSNotification *note))block NS_AVAILABLE(10_6, 4_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.

最後のメソッドはNSObserverオブジェクトを返します.このメソッドを使用してオブジェクトを追加する場合は、ループ参照とremoveの方法に注意する必要があります.各方法の異なるシステムでの表現も異なる.

一、observerを追加する方法


1、SEL方式
- (void)addObserver:(id)observer selector:(SEL)aSelector name:(nullable NSNotificationName)aName object:(nullable id)anObject;

このメソッドのobserverは弱いリファレンスであり、selfなどを直接使用しても強いリファレンスは発生しません.ここで、同じ通知がn回連続して追加されると、SELのメソッドはn回呼び出されるので、addObserverとremoveObserverのペアが現れることを確認します.例:
- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(test) name:@"test" object:nil];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(test) name:@"test" object:nil];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(test) name:@"test" object:nil];
}

- (void)test
{
  
}

test通知を送信すると、testメソッドが3回呼び出されます.
2、Block方式
- (id )addObserverForName:(nullable NSNotificationName)name object:(nullable id)obj queue:(nullable NSOperationQueue *)queue usingBlock:(void (^)(NSNotification *note))block NS_AVAILABLE(10_6, 4_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.

この方法では、いくつかの点に注意してください.1)この方法は、システムによって強く保持されているNSObserverオブジェクトを返します.呼び出し元は、観察者の削除を通知するときに使用するオブジェクトを保持する必要があります.このようにして追加された通知は、このオブジェクトを持たなければ、この通知を完全に破棄することはできません.具体的には、次のようにします.
  @property (nonatomic, strong) id observer;

  - (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    //  
    _observer = [[NSNotificationCenter defaultCenter] addObserverForName:@"test" object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification * _Nonnull note) {
        
    }];
  }      

  - (void)dealloc {
      if (_observer) {
          [[NSNotificationCenter defaultCenter] removeObserver:_observer];
          _observer = nil;
      }
  }


2)このメソッドのblockはオブジェクトを強く持つため、blockのメンバー変数と属性には弱い参照が必要であり、弱い参照がない場合はdealloc関数の前に通知を削除する必要があります.そうしないとdealloc関数は実行されません.
  @property (nonatomic, strong) id observer;

  - (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.

    //  
    __weak typeof(self) weakSelf = self;
    //  
    _observer = [[NSNotificationCenter defaultCenter] addObserverForName:@"test" object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification * _Nonnull note) {
        weakSelf.label.backgroundColor = [UIColor redColor];
    }];
  }      

  - (void)dealloc {
      if (_observer) {
          [[NSNotificationCenter defaultCenter] removeObserver:_observer];
          _observer = nil;
      }
  }

二、通知の発送方法


通知の送信方法は主に以下の3つです.
- (void)postNotification:(NSNotification *)notification;
- (void)postNotificationName:(NSNotificationName)aName object:(nullable id)anObject;
- (void)postNotificationName:(NSNotificationName)aName object:(nullable id)anObject userInfo:(nullable NSDictionary *)aUserInfo;

1、ここで注意しなければならないのは、通知を送信するとき、存在するスレッドであり、サブスレッドで通知を送信する場合、通知の受信もサブスレッドである.2、通知中のobjectパラメータを送るときは注意が必要です.これは登録時に使う方法と関係があります.登録方法には、objectパラメータもあります.栗を挙げます.
//  
  self.name = @"Tom";
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(test1) name:@"lala" object:_name];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(test2) name:@"lala" object:nil];

上の2つの登録通知の名前はlalaで、唯一の違いはobjectがnilで、1つは_name.次の2つの方法で通知を送信します.1つ目は、
[[NSNotificationCenter defaultCenter] postNotificationName:@"lala" object:nil];

この方式で通知を送信する場合,test 2メソッドのみが呼び出される.2つ目:
[[NSNotificationCenter defaultCenter] postNotificationName:@"lala" object:_name];

この方法で通知を送信するとtest 1とtest 2が呼び出されます.もしこの時私が_nameが変わったらどうなるの?
_name = @"lala";
[[NSNotificationCenter defaultCenter] postNotificationName:@"lala" object:_name];

このときtest 2のみが呼び出されます.nameのアドレスが変更されたため通知が一致せずtest 1が認識できなくなった.以上、通知を追加するときにobjectパラメータが渡されると、通知を送信するときにnameとobjectの2つの条件が一致することを説明した.objectパラメータが渡されていない場合は、nameのみが一致します.
突然,除去方法にもこのobjectパラメータがあることを発見し,構想の一貫性のために,除去通知の方法をここに書いた.上記の方法に従って、2つの通知が追加されました.1つはobjectパラメータを携帯し、1つはobjectパラメータを携帯しません.まず、以下の操作で削除します.
[[NSNotificationCenter defaultCenter] removeObserver:self name:@"lala" object:_name];

この場合、test 1メソッド通知は削除されたが、test 2メソッドは削除されなかった.削除する前に変更した場合_名前は?
_name = @"lala ";
[[NSNotificationCenter defaultCenter] removeObserver:self name:@"lala" object:_name];

このときの効果は同じであることがわかります.nameのアドレスは変化したがtest 1の通知は除去され,test 2の通知は除去されなかった.次の方法で削除します.
[[NSNotificationCenter defaultCenter] removeObserver:self name:@"lala" object:nil];

このときtest 1とtest 2はともに除去された.使用する場合
[[NSNotificationCenter defaultCenter] removeObserver:self];

効果は同じで、この方法は自分が追加した通知方法を削除するだけでなく、システムの通知方法も削除するので、このオブジェクトが解放されない限り、簡単に削除通知を使用しないでください.
まとめてみます.
1、通知を追加するときにobjectパラメータが渡されると、通知を送信するときにnameとobjectの2つの条件が一致します.objectパラメータが渡されていない場合は、nameのみが一致します.2、objectオブジェクトを途中で変更すると、[[NSNotificationCenter defaultCenter] postNotificationName:@"lala" object:_name];で送信された通知は無効になります.3、通知を削除するとき、removeのnameとobjectが存在する場合、このように初期化され、nameが一致して削除されます.removeにnameしかない場合、このnameの下のすべての通知が削除されます.4、[[NSNotificationCenter defaultCenter] removeObserver:self];は、自分が追加した通知だけでなく、システムが自動的に追加した通知も削除するので、クラスが解放されない限り、この方法を慎重に使用します.

三、通知を削除する方法


通知の送信について説明したように、通知を削除するには主に次の2つの方法があります.
- (void)removeObserver:(id)observer;
- (void)removeObserver:(id)observer name:(nullable NSNotificationName)aName object:(nullable id)anObject;

四、システムの違い


NSNotificationCenterに分類を追加し、システムの削除通知方法をhookする
+ (void)load
{
    Method originRemoveM = class_getInstanceMethod([self class], @selector(removeObserver:));
    Method myRemoveM = class_getInstanceMethod([self class], @selector(my_removeObserver:));
    method_exchangeImplementations(originRemoveM, myRemoveM);
}

- (void)my_removeObserver:(id)observer
{
    NSLog(@"  -> observer = %@", observer);
    
    [self my_removeObserver:observer];
}

ログ印刷により、iOS 8以上のシステムでは、ViewControlクラスがdeallocの後、removeObserverメソッドを自動的に呼び出すことがわかります.
iOS 9システム以降、オブジェクトが解放された場合、SEL方式で追加された場合、通知を削除しなければ影響はありません.iOS 9システム以前に、オブジェクトが解放されたときに削除されなければ、オブジェクトが解放された後に通知を送信すると、野ポインタがクラッシュする可能性があります.削除することをお勧めします.Block方式で追加する場合は、必ずオブジェクトを持ち、解放時に自分で手動で解放操作を行います.そうしないとオブジェクトが解放されますが、通知を送信するとblockが呼び出され、bolckのselfオブジェクトはnilになります.
以上、お知らせセンターのご利用についてご自身でご理解いただけましたので、間違いや漏れがございましたら、ご伝言をお願いいたします.