What is the NSNotificationCenter

NSNotificationCenter is a notification scheduling mechanism that allows information to be broadcast to registered observers.

A message can be received by multiple observers, and an observer can receive multiple messages.

NSNotificationCenter can only deliver notifications within a single application; If you want to publish notice to the other process or receive notifications from other processes, please use the NSDistributedNotificationCenter (note only on macOS 10.0 + can be used, do not use in iOS).

Use of NSNotificationCenter in detail

Before using the guidelines

Notification consists of three elements: notification name (mandatory), sender (optional), and content (optional). Just like sending an email, you must specify the subject of the email (corresponding to the notification name), otherwise you are not allowed to send it. The sender and content may not be specified.

The API also states that the name property cannot be nil.

The use of API

Add observers and subscribe to notifications

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

    When name is null, notifications of any name are subscribed.

    When object is null, any sender subscribes to notifications.

    If both name and object are empty, the system subscribes to all notifications, including system notifications. You can use this method to listen to all system notifications.

    When neither name nor object is empty, only the specified notification name and sender are subscribed to.

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

Send a notification

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

    The correct posture for creating NSNotification:

    • +(instancetype)notificationWithName:(NSNotificationName)aName object:(nullable id)anObject; –(Note: name is mandatory)
    • +(instancetype)notificationWithName:(NSNotificationName)aName object:(nullable id)anObject userInfo:(nullable NSDictionary *)aUserInfo; –(Note: name is mandatory)
    • -(instancetype)initWithName:(NSNotificationName)name object:(nullable id)object userInfo:(nullable NSDictionary *)userInfo; –(Note: name is mandatory)
  • -(void)postNotificationName:(NSNotificationName)aName object:(nullable id)anObject; –(Note: name is mandatory)

  • -(void)postNotificationName:(NSNotificationName)aName object:(nullable id)anObject userInfo:(nullable NSDictionary *)aUserInfo; –(Note: name is mandatory)

Delete the observer and unsubscribe

  • -(void)removeObserver:(id)observer;

    The observer mandatory field unsubscribes all subscriptions to the current observer object.

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

    Observer Mandatory field, representing the current observer object.

    An empty name means that notifications by any name are unsubscribed.

    Object is empty, indicating that notifications from any sender are unsubscribed.

    Both name and object are empty, equivalent to -(void)removeObserver:(id)observer, which cancels all subscriptions in the current observer object.

    Neither name nor object is empty, which means that notifications with the specified name and sender are unsubscribed.

Considerations for NSNotificationCenter

Do YOU need to remove the observer added by target mode?

Official documentation:

If your app targets iOS 9.0 and later or macOS 10.11 and later, you don’t need to unregister an observer in its dealloc method. Otherwise, you should call removeObserver:name:object: before observer or any object passed to this method is deallocated.

Is iOS 9.0 later, do not need in the dealloc method removed the observer, or need to call in the dealloc removeObserver: name: object: method is removed.

Observations of the Observer added using block mode

A strong reference to self cannot be called within a block, otherwise a memory leak will occur

Because NSNotificationCenter holds the Block, and the block holds the Observer (the current ViewController), the current ViewController cannot be released.

You need to remove it manually, otherwise the block stays in the NSNotificationCenter

Official documentation:

The block is copied by the notification center and (the copy) held until the observer registration is removed.

That is, the block will remain in place until manually removed.

Verification mode:

Operation steps: 1, ViewControllerA push -> ViewControllerB 2, ViewControllerB pop -> ViewControllerA 3, ViewControllerA sends notifications Add observer in ViewControllerB viewDidLoad:  self.testObserver = [[NSNotificationCenter defaultCenter] addObserverForName:@"TestName" object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification * _Nonnull note) {
   NSLog(@"note = %@", note); }]; ViewControllerB dealloc adds a Log to the dealloc method to check whether ViewControllerB is released properly: NSLog(@)"%s", __func__); ViewControllerA Clicks the button to send a notification: [[NSNotificationCenter defaultCenter] postNotificationName:@"TestName"object:nil]; After ViewControllerB is released, a note can be printed indicating that the block is not released.Copy the code

Correct posture:

__weak typeof(self) weakSelf = self;
self.testObserver = [[NSNotificationCenter defaultCenter] addObserverForName:@"TestName" object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification * _Nonnull note) {
    
    __strong typeof(weakSelf) self = weakSelf;
    if(! self)return; //TODO write concrete logic}]; - (void) dealloc {/ / must release [[NSNotificationCenter defaultCenter] removeObserver: self. TestObserver]; }Copy the code

Read the source code

Gnustep source

www.gnustep.org/resources/d… Download GNUstep Base 1.26.0. The code is relatively simple, with the following core data structure chart to read yourself.

Nameless: hash table with key object and value observer collection (linked list storage). Store all object – only observer information.

Name: hash table with key name and value object. Stores all observer information whose name cannot be empty.

Wildcard: Collection of observers. Store observer information with null name and object.

Apple source

Github.com/apple/swift…

The code is relatively simple, read by yourself.

Do it yourself (see Apple open Source implementation)

JBNotificationCenter.h

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

typedef NSString* JBNotificationName;

@interface JBNotification : NSObject

+ (instancetype)notificationWithName:(JBNotificationName)aName
                              object:(nullable id)anObject;

+ (instancetype)notificationWithName:(JBNotificationName)aName
                              object:(nullable id)anObject
                            userInfo:(nullable NSDictionary *)aUserInfo;


@end



@interface JBNotificationCenter : NSObject 

@property (class, readonly, strong) JBNotificationCenter *defaultCenter; /** Add observer @param observer @param aSelector callback method @param aName Notification name @param anObject Notification associated object */ - (void)addObserver:(id)observer selector:(SEL)aSelector name:(nullable JBNotificationName)aName object:(nullable id)anObject; /** @param notification notification object */ - (void)postNotification:(JBNotification *)notification; /** send notification @param aName notification name @param anObject notification associated object */ - (void)postNotificationName:(JBNotificationName)aName object:(nullable id)anObject; @param aName Notification name @param anObject Notification associated object @param aUserInfo parameter */ - (void)postNotificationName:(JBNotificationName)aName object:(nullable id)anObject userInfo:(nullable NSDictionary *)aUserInfo; /** removeObserver @param observer */ - (void)removeObserver:(id)observer; /** delete observer @param observer @param aName notification name @param anObject notification associated object */ - (void)removeObserver:(id)observer name:(nullable JBNotificationName)aName object:(nullable id)anObject; /** Add observer @param name notification name @param obj Notification associated object @param queue Notification queue @param block callback @return*/ - (id <NSObject>)addObserverForName:(nullable NSNotificationName)name object:(nullable id)obj queue:(nullable NSOperationQueue *)queue usingBlock:(void (^)(JBNotification *note))block; @end NS_ASSUME_NONNULL_ENDCopy the code

JBNotificationCenter.m

#import "JBNotificationCenter.h"@interface JBNotificationReceiver : NSObject @property(nonatomic, weak) id observer; // Observer @property(nonatomic, assign) SEL selector; // Observer callback selector @property(nonatomic, copy) NSString *name; @property(nonatomic, weak) id sender; // @end@implementation JBNotificationReceiver @end@interface JBNotification () @property(nonatomic, copy) NSString *name; @property(nonatomic, strong) id object; @property(nonatomic, strong) NSDictionary *userInfo; @end @implementation JBNotification + (instancetype)notificationWithName:(JBNotificationName)aName object:(nullable id)anObject {return [self notificationWithName:aName object:anObject userInfo:nil];
}





+ (instancetype)notificationWithName:(JBNotificationName)aName
                              object:(nullable id)anObject
                            userInfo:(nullable NSDictionary *)aUserInfo {
    
    JBNotification *notification = [[JBNotification alloc] init];
    notification.name = aName;
    notification.object = anObject;
    notification.userInfo = aUserInfo;
    
    return notification;
}

- (NSString *)description {
    return [NSString stringWithFormat:@"%@ %p {name = %@; object = %@}",self.class,self, self.name, self.object];
}

@end

typedef void (^JBNotificationBlock)(JBNotification *note);

@interface JBBlockNotificationObserver : NSObject

@property(nonatomic, weak) NSOperationQueue *queue;

@property(nonatomic, copy) JBNotificationBlock block;


- (id) initWithQueue: (NSOperationQueue *)queue block: (JBNotificationBlock)block;


@end

@implementation JBBlockNotificationObserver


- (id) initWithQueue: (NSOperationQueue *)queue block: (JBNotificationBlock)block {
    self = [super init];
    if (self) {
        self.queue = queue;
        self.block = block;
    }
    return self;
}

- (void) didReceiveNotification: (JBNotification *)note {
    
    if(self.queue && self.queue ! = NSOperationQueue.currentQueue) { [self.queue addOperationWithBlock:^{ self.block(note); }]; [self.queuewaitUntilAllOperationsAreFinished];
        
    } else {
        self.block(note);
    }
    
    
}

@end


@interface JBNotificationCenter ()

@property(nonatomic, strong) NSMutableArray<JBNotificationReceiver *> *observers;

@property(nonatomic, strong) NSLock *observerLock;



@end
@implementation JBNotificationCenter

+ (instancetype)defaultCenter {
    
    static id instance = nil;
    
    static dispatch_once_t onceToken;
    
    dispatch_once(&onceToken, ^{
        instance = [[self alloc] init];
    });
    
    return instance;
}

- (instancetype)init {
    self = [super init];
    if (self) {
        self.observers = [[NSMutableArray alloc] initWithCapacity:0];
        self.observerLock = [[NSLock alloc] init];
    }
    returnself; } /** Add observer @param observer @param aSelector callback method @param name Notification name @param Object Notification associated object */ - (void)addObserver:(id)observer selector:(SEL)aSelector name:(nullable JBNotificationName)name object:(nullable id)object {// Validate parametersif (observer == nil)
        [NSException raise: NSInvalidArgumentException
                    format: @"Nil observer passed to addObserver ..."];
    
    if (aSelector == 0)
        [NSException raise: NSInvalidArgumentException
                    format: @"Null selector passed to addObserver ..."];
    
    if ([observer respondsToSelector: aSelector] == NO){
        [NSException raise: NSInvalidArgumentException
                    format: @"[%@-%@] Observer '%@' does not respond to selector '%@'", NSStringFromClass([self class]), NSStringFromSelector(_cmd), observer, NSStringFromSelector(aSelector)]; } [self.observerLock lock]; JBNotificationReceiver *notificationReceiver = [[JBNotificationReceiver alloc] init]; notificationReceiver.observer = observer; notificationReceiver.selector = aSelector; notificationReceiver.name = name; notificationReceiver.sender = object; [self.observers addObject:notificationReceiver]; [self.observerLock unlock]; } /** Add observer @param name Notification name @param object Notification associated object @param Queue Notification queue @param block callback @return*/ - (id <NSObject>)addObserverForName:(nullable NSNotificationName)name object:(nullable id)object queue:(nullable NSOperationQueue *)queue usingBlock:(void (^)(JBNotification *note))block { [self.observerLock lock]; JBNotificationReceiver *notificationReceiver = [[JBNotificationReceiver alloc] init]; JBBlockNotificationObserver *observer = [[JBBlockNotificationObserver alloc] initWithQueue:queue block:block]; notificationReceiver.observer = observer; notificationReceiver.selector = @selector(didReceiveNotification:); notificationReceiver.name = name; notificationReceiver.sender = object; [self.observers addObject:notificationReceiver]; [self.observerLock unlock];returnobserver; } /** send notification @param name notification name @param object notification associated object */ - (void)postNotificationName:(JBNotificationName)name object:(nullable id)object { [self postNotificationName:name object:object userInfo:nil]; } /** Send notification @param name Notification name @param object Notification associated object @param userInfo parameter */ - (void)postNotificationName:(JBNotificationName)name object:(nullable id)object userInfo:(nullable NSDictionary *)userInfo { JBNotification *notification = [JBNotification notificationWithName:name object:object userInfo:userInfo]; [self postNotification:notification]; } /** send notification @param notification notification object */ - (void)postNotification:(JBNotification *)notification {// find a qualified observer [self.observerLock lock]; NSMutableArray<JBNotificationReceiver *> *sendTo = [[NSMutableArray alloc] init];for (JBNotificationReceiver *receiver in self.observers) {
        
        if ((receiver.name == nil || [receiver.name isEqualToString:notification.name])
            &&(receiver.sender == nil || [receiver.sender isEqual:notification.object])) {
            
            [sendTo addObject:receiver];
        }
        
    }
    [self.observerLock unlock];
    
    
    NSMutableArray<JBNotificationReceiver *> *delete = [[NSMutableArray alloc] init];
    
    for (JBNotificationReceiver *receiver in sendTo) {
        if (receiver.observer) {
            #pragma clang diagnostic push
            #pragma clang diagnostic ignored "-Warc-performSelector-leaks"
                         [receiver.observer performSelector:receiver.selector withObject:notification];
            #pragma clang diagnostic pop
        } else{ [delete addObject:receiver]; } } [self.observerLock lock]; [self.observers removeObjectsInArray:delete]; [self.observerLock unlock]; } /** delete observer @param Observer */ - (void)removeObserver:(id)observer {[self removeObserver: Observer name:nil object:nil]; } /** delete observer @param observer @param name notification name @param object notification associated object */ - (void)removeObserver:(id)observer name:(nullable JBNotificationName)name object:(nullable id)object { [self.observerLock lock]; NSMutableArray<JBNotificationReceiver *> *keep = [[NSMutableArray alloc] init];for (JBNotificationReceiver *receiver inSelf.observers) {// Notify other objects, leave themif(receiver.observer ! = nil && receiver.observer ! = observer) { [keep addObject:receiver];continue; } // If the notification object is the same, the current name is not empty and the name is differentif(name ! = nil && ! [name isEqualToString:receiver.name]) { [keep addObject:receiver];continue; } // If the notification name is the same, the current sender is not empty and the sender is different, leaveif(object ! = nil && receiver.observer ! = object) { [keep addObject:receiver]; } } self.observers = keep; [self.observerLock unlock]; } @endCopy the code