This is the second day of my participation in the First Challenge 2022

Notifications are a programming framework that Foundation provides for passing information about the occurrence of events. This document describes the elements of the architecture and explains how to use them. Then inform related knowledge from the third part analyzes the notifications, notification centers, notification of the queues.

Notifications

An overview of the

Notification is information that encapsulates an event, and objects that need to know about the event (for example, a file that needs to know when its window closes) register with the notification center expecting to be notified when the event occurs. When an event does occur, the notification center publishes a notification, which is immediately broadcast to all registered objects. Alternatively, notifications are queued in a notification queue, which, after deferring the notification, publishes it to the notification center and consolidates similar notifications based on some specified criteria that you specify.

Notifications and Their Rationale

Differentiating the standard form of messaging (a method by which one object calls another), the broadcast model is introduced in cases where objects in relatively independent subsystems are linked together: An object publishes a notification, which is dispatched to the appropriate observer via an NSNotificationCenter object or simple notification center. An NSNotification object (called a notification) contains a name, an object, and an optional dictionary. A name is a label that identifies a notification. An object is any object that the publisher of the notification wants to send to the observer of the notification — usually the object that publishes the notification itself. Dictionaries may contain additional information about events.

Notification and Delegation

Using a notification system is similar to using delegates, but with the following differences:

  • Any number of objects can be notified, not just delegate objects.
  • An object can receive any message you need from the notification center, not just the predefined delegate methods.
  • The object issuing the notification does not even have to know that the observer exists.

Notification Centers

The notification center manages the sending and receiving of notifications. It notifies all observers of notifications that meet certain criteria. Notification information is encapsulated in an NSNotification object. Client objects register themselves with the notification center as observers of specific notifications published by other objects. When an event occurs, the object publishes the appropriate notification to the notification center. The notification center sends a message to each registered observer, passing the notification as the only parameter. The published object and the observed object may be the same. There are two types of Notification centers included in the Cocoa framework:

  •  NSNotificationCenterSingle process notification center class, commonly used in development.
  •  NSDistributedNotificationCenterNotification center class for multi-process management.

NSNotificationCenter

Each process has a default notification center that you can access using the NSNotificationCenter +defaultCenter class method. This notification center handles notifications in a single process. For communication between the process of the same machine, the use of distributed notification center (see NSDistributedNotificationCenter).

The notification center synchronously sends notifications to the observer. In other words, when a notification is published, control does not return to the publisher until all observers have received and processed the notification. To send notifications asynchronously, use Notification Queues, as described in Notification Queues.

In multithreaded applications, notifications are always delivered in the thread that issued the notification, which may be different from the observer registering its own thread.

NSDistributedNotificationCenter

Each process has a default distributed notification center, you can use NSDistributedNotificationCenter + defaultCenter class methods to access it. This distributed notification center handles notifications that can be sent between processes on a single machine. For communication between processes on different machines, use Distributed Objects (see Distributed Objects Programming Topics).

Notification Queues

The NSNotificationQueue object, or simply, the notification queue, acts as a buffer for the notification center (instance of NSNotificationCenter). The NSNotificationQueue class contributes two important features to Foundation Kit’s notification mechanism: notification merging and asynchronous publishing.

Notification Queue Basics

Using the postNotification: method of THE NSNotificationCenter and its variants, you can publish notifications to the notification center. However, the invocation of this method is synchronous: before the published object can resume its thread of execution, it must wait until the notification center dispatches the notification to all observers and returns. Notification queues, on the other hand, typically maintain notifications (an instance of NSNotification) in a first-in, first-out (FIFO) order. When a notification rises to the front of the queue, the queue publishes it to the notification center, which in turn dispatches the notification to all objects registered as observers.

Each thread has a default notification queue, which is associated with the default notification center for the process. You can create your own notification queues and have multiple queues per center and thread.

Posting Notifications Asynchronously

Using NSNotificationQueue enqueueNotification: postingStyle: and enqueueNotification: postingStyle: coalesceMask: forModes: Method to asynchronously publish notifications to the current thread by putting them on a queue. These methods return the calling object immediately after putting the notification into the queue.

Note: No notification is published if the notification enqueued thread terminates before the notification queue publishes the notification to its notification center.

The notification queue is emptied and its notifications are published according to the publication style and Runloop mode specified in the enqueue method. The mode parameter specifies the runloop mode in which queues will be emptied. For example, if you specify NSModalPanelRunLoopMode, notifications will only be published if runloop is in this mode. If runloop is not currently in this mode, the notification will wait until the next time it enters this mode.

Can be released in one of three different ways to the notification queue: NSPostASAP, NSPostWhenIdle and NSPostNow.

Posting As Soon As Possible

Assuming that the current Runloop mode matches the pattern of the request, any notifications queued in NSPostASAP style will be published to the notification center when the current iteration of the Runloop is complete (notifications will be published when the request mode is entered if the request mode differs from the current one). Because Runloop can be called multiple times during each iteration, notifications may or may not be delivered immediately when the current call exits and control returns to runloop. Other annotations may occur first, such as timers or source triggers or other asynchronous notifications being delivered.

The NSPostASAP publishing style is typically used for expensive resources, such as display services. While many clients draw on the window buffer during runloop calls, it is costly to flush the buffer to the display server after each draw operation. In this case, each draw… Methods will queue some notifications, such as “FlushTheServer”, which contains the merge of the specified name and object and the publication style of NSPostASAP. As a result, only one notification is dispatched at the end of the runloop, and the window buffer is flushed only once.

Posting When Idle

Notifications queued in NSPostWhenIdle style are published only when runloop is in a wait state. In this state, there is nothing in the input channel of the Runloop, either a timer or any other asynchronous event. A typical example of queueing in the NSPostWhenIdle style occurs when the user types text and the program displays the size of the text in bytes somewhere. Updating the text field size after the user types each character can be expensive (and not very useful), especially if the user types quickly. In this case, the program queues up a notification, such as “ChangeTheDisplayedSize”, and turns on the merge and uses the NSPostWhenIdle publishing style after each character is typed. When the user stops typing, a single “ChangeTheDisplayedSize” notification in the queue (due to merging) is published when runloop enters its wait state and updates the display. Note that the runloop that is about to exit (which occurs when all input channels have expired) is not in a wait state and therefore will not be notified.

Posting Immediately

Notifications of the NSPostNow type are published immediately after being merged into the notification center. When asynchronous invocation behavior is not required, you can queue notifications using NSPostNow (or use postNotification). In some cases, a synchronous approach is used, for example, where you want the notification center to return after scheduling so that you can be sure that the observation object has received and processed the notification. To do this, use enqueueNotification… And NSPostNow instead of using postNotification.

Coalescing Notifications(Merge)

In some cases, you may want to publish notifications if a given event occurs at least once, but you may want to publish no more than one notification even if the event occurs multiple times. For example, in an application that receives data as discrete packets, you might want to publish a notification indicating that data needs to be processed after the packet is received. However, if multiple packets arrive in a given period of time, you do not want to publish multiple notifications. In addition, the object publishing these notifications may not know if more packets are coming or if the publishing method is called in the loop. Therefore, instead of publishing notifications to the notification center, you can add notifications to an NSNotificationQueue instance that specifies the appropriate merge options. Merge is the process of removing notifications from a queue that are somewhat similar to previously queued notifications. You can through the enqueueNotification: postingStyle: coalesceMask: forModes: method of the third parameter specified in the following one or more constants to indicate that the similarity criteria.

Can use NSNotificationCoalescingOnName NSNotificationCoalescingOnSender constants and perform bitwise or operations, to use the notification object name and notice specifying the merger. The following example shows how queues can be used to ensure that all notifications named MyNotificationName are merged into one notification during a given event cycle.

   // MyNotificationName defined globally
   NSString *MyNotificationName = @"MyNotification";
   id object = <#The object associated with the notification#>;
   NSNotification *myNotification =[NSNotification  notificationWithName:MyNotificationName object:object];
   [[NSNotificationQueue defaultQueue] enqueueNotification:myNotification postingStyle:NSPostWhenIdle coalesceMask:NSNotificationCoalescingOnName forModes:nil];
Copy the code

Registering for a Notification

Registering Notifications in your own app or in any other app can deal with Registering for Local Notifications and Registering for Distributed Notifications, respectively.

Registering for Local Notifications

Call notification center method addObserver: selector: name: object: register an object to receive notifications, specify the observer, notify the center should be sent to the news of the observer, it wants to receive notification of name, as well as about which object.

  • You do not need to specify both the name and the object. If you specify only one object, the observer will receive all notifications containing that object. If you specify only the notification name, the observer will receive the notification every time it is published, regardless of the object associated with it.
  • Observers can register to receive multiple messages for the same notification. In this case, the observer will receive all the messages it is registered to receive, but cannot determine the order in which they are received.
  • If you decide that an observer no longer needs to receive notifications (for example, if the observer is being released), you can use methodsremoveObserver:removeObserver:name:object:Removes the observer from the list of observers in the notification center.

Typically, objects are registered using the process’s default notification center. Use the defaultCenter class method to get the default object. As shown in the following example:

/ / by transfer nil as object to observe, when any object NSWindowDidBecomeMainNotification notice, [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(aWindowBecameMain:) name:NSWindowDidBecomeMainNotification object:nil]; // When the window becomes the main window, It would be to notify the center issued a NSWindowDidBecomeMainNotification - (void) aWindowBecameMain: (NSNotification *) notification {NSWindow *theWindow = [notification object]; MyDocument = (MyDocument *)[[theWindow windowController] document]; // Retrieve information about the document and update the panel. }Copy the code

Registering for Distributed Notifications

Object through send NSDistributedNotificationCenter object addObserver: selector: name: object: suspensionBehavior: Method to register itself to receive notifications, specifying the message the notification should send, the name of the notification it wants to receive, the identity string to match (object parameters), and the behavior to follow when notification delivery is paused.

This type of distributed notification is used for interprocess communication on macOS and is not supported on iOS.

Unregistering an Observer

Before an object observing notifications can be released, it must tell the notification center to stop sending notifications to it. Otherwise, the next notification will be sent to a nonexistent object and the program crashes (the _unsafe_unretain modified object frees the crash caused by the access).

No matter how many objects and notifications it registers, the following method removes them all:

[[NSNotificationCenter defaultCenter] removeObserver:self];
Copy the code

More specific removeObserver with the notification name and observation object specified… Method to selectively unregister a particular notification object.

Posting a Notification

There are also two types of Local NotificationsPosting and Distributed Notifications for registering a notification

Posting Local Notifications

Using notificationWithName: object: or notificationWithName: object: the userInfo: create notification object. The notification object is then published to the notification center using the postNotification: instance method. The NSNotification object is immutable, so once created, it cannot be modified.

// addObserver [[NSNotificationCenter defaultCenter]addObserver:self selector: @selector(didrecieve) name:@"123" object:@"ios"]; -(void)didrecieve{NSLog(@"Observe1 received notification "); } -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- / / notifications [[NSNotificationCenter defaultCenter]postNotificationName:@"123" object:@"ios" userInfo:@{}];Copy the code

When adding an observer (addobServer), if an object(@”ios”) is passed in, the observer is notified only if the object in postNotificationName is the same object(@”ios”). When adding an observer (AddobServer), if object=nil is passed in, postNotificationName will be notified as long as the name value corresponds to the same value. Name identifies the notification, and Object identifies the notification accurately.

Posting Distributed Notifications

slightly

Threads and Notifications

By default, the message is delivered in the thread of the message POST. Sometimes, notifications may need to be delivered on specific threads determined by you rather than the notification center, for example, if an object running in a background thread is listening for notifications from the user interface, such as a window closing, and you want to receive notifications in the background thread rather than the main thread. In these cases, you must capture the notifications delivered on the default thread and redirect them to the appropriate thread.

The idea for thread redirection is to use a custom notification queue (not an NSNotificationQueue object) to store any notifications received on the wrong thread and then process them on the correct thread. The technique works by registering notifications and, when they arrive, testing whether the current thread is the one that should handle them. If it is the wrong thread, the notification is stored in a queue and then a signal is sent to the correct thread indicating that the notification needs to be processed. Another thread receives the signal, removes the notification from the queue, and processes the notification.

The observer needs an instance variable with the following values:

  • A mutable array for holding notifications;
  • The communication port used to signal the correct thread (Mach port);
  • Locks to prevent multiple threads from colliding with notification arrays;
  • And a value that identifies the correct thread (an NSThread object);
#import <Foundation/Foundation.h>

@interface  MyThreadedClass : NSObject

/* Threaded notification support. */

@property NSMutableArray *notifications;

@property NSThread *notificationThread;

@property NSLock *notificationLock;

@property NSMachPort *notificationPort;

@end
Copy the code

Implement related

#import "MyThreadedClass.h" @interface** MyThreadedClass ()<NSMachPortDelegate> @end @implementation** MyThreadedClass -(instancetype)init{ if (self = [super init]) { [self setUpThreadingSupport]; [[NSNotificationCenter defaultCenter]addObserver:self selector: @selector(processNotification:) name:@"NotificationName" object:nil]; } return self; } // Initialize related variables - (void)setUpThreadingSupport {if (self.notifications) {return; } self.notifications = [[NSMutableArray alloc] init]; self.notificationLock = [[NSLock alloc] init]; // self.notificationThread = [NSThread currentThread]; self.notificationPort = [[NSMachPort alloc] init]; [self.notificationPort setDelegate:self]; / / added to the runloop [[NSRunLoop currentRunLoop] addPort: self. NotificationPort forMode: NSRunLoopCommonModes]; } // the machport callback - (void)handleMachMessage:(void*) MSG {[self.notificationLock lock]; while ([self.notifications count]) { NSNotification *notification = [self.notifications objectAtIndex:0]; [self.notifications removeObjectAtIndex:0]; [self.notificationLock unlock]; [self processNotification:notification]; [self.notificationLock lock]; }; [self.notificationLock unlock]; } // the callback of the notification is observed - (void)processNotification:(NSNotification *)notification {// determine whether the thread that sent the notification is the thread that is expected to receive the callback, if notif ([NSThread currentThread] ! = _notificationThread) { // Forward the notification to the correct thread. [self.notificationLock lock]; [self.notifications addObject:notification]; [self.notificationLock unlock]; [self.notificationPort sendBeforeDate:[NSDate date] components:nil from:nil reserved:0]; } else {//dosomething // Process the notification here; } } @endCopy the code