problems

Typically we set up an instance method to listen for NSNotification, and then we use the instance method to process the message.

So can we listen for notifications in class methods, and can we use class methods to handle notifications?

Validation step

Initialize a Parents class and send a notification named Study

Parents class

- (void) postNotification {
    [[NSNotificationCenter defaultCenter] postNotificationName:@"study" object:nil];
}
Copy the code

Case 1: class method listens, class method handles

Student class

+ (void) addListener {
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(hanleNotification:) name:@"study" object:nil];
}

+ (void) hanleNotification:(NSNotification*) notification {
    NSLog(@" Time to learn")} - (void) dealloc {
    NSLog(@" Object Destruction");
    [[NSNotificationCenter defaultCenter] removeObserver:self];
}

Copy the code

Set the listener for the notification named study in the class method of the Student class, and set the notification handling method to the class method

+ (void) hanleNotification:(NSNotification*) notification
Copy the code

You can receive notifications in the Student class and print them out. And no matter how many Student objects are instantiated and destroyed later, the listener in the class method will still exist and will still be able to handle notifications.

Case 2: class method listens, instance method handles

+ (void) addListener {
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(hanleNotification:) name:@"study" object:nil];
}

- (void) hanleNotification:(NSNotification*) notification {
    NSLog(@" Time to learn")} - (void) dealloc {
    NSLog(@" Object Destruction");
    [[NSNotificationCenter defaultCenter] removeObserver:self];
}
Copy the code

After the notification is sent, the student receives the notification, but no action can be found.

Case three: instance method listens, class method handles

- (void) addListener {
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(hanleNotification:) name:@"study" object:nil];
}

+ (void) hanleNotification:(NSNotification*) notification {
    NSLog(@" Time to learn")} - (void) dealloc {
    NSLog(@" Object Destruction");
    [[NSNotificationCenter defaultCenter] removeObserver:self];
}
Copy the code

Parents instantiates a Student object that calls instance methods to listen for notifications.

- (void) postNotification {
    // Instantiate a Student object and listen for notifications using instance methods
    Student stu = [[Student alloc] init];
    [stu addListener];

    [[NSNotificationCenter defaultCenter] postNotificationName:@"study" object:nil];
}
Copy the code

When a notification is sent in parents, the instance object STU can receive the notification, but no method is found to execute it

The principle of analytic

Method invocation

In OC, the method is in fact the call to send message objects, if the object of this class can’t find the processing method, will be the inheritance chain to the up find the parent class, until you find the root class, if the root class will also can not find processing method for message forwarding, if you don’t have to process the message object will be an error, can’t find the message processing method.

Methods of instance objects and class objects are saved

In OC, the instance object is actually constructed from the corresponding class of the instance object according to the ISA pointer, so the instance method in the instance object is stored in its class, there is only one copy. The instance object itself does not hold methods.

Similarly, the class itself is an object, and it is constructed by the metaclass that finds its corresponding metaclass according to its own ISA pointer. So the class methods are stored in the metaclass, so the class object itself does not hold the class methods.

NSNotificationCenter Sends notifications

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(hanleNotification:) name:@"study" object:nil];
Copy the code

[NSNotificationCenter defaultCenter] is a global singleton that generates a callback queue based on the name of the listener passed in.

If the listener is registered in an instance method, then this registered self represents the observer as the instance object.

If the listener is registered in a class method, then this registered self represents the observer as a class object

[[NSNotificationCenter defaultCenter] postNotificationName:@"study" object:nil];
Copy the code

When you send a notification, what you’re doing is you’re going to find the corresponding callback queue based on the name property, and you’re going to send a message to the registered observer object in the callback queue that’s going to process the notification method, which is the method that’s set in the selector:@selector(hanleNotification).

NamedTable and UnNamed Table notification Center NSNotificationCenter

NotificationCenter, NSNotificationCenter, saves the implementation of the observer by name.

NSNotificationCenter stores the structure of the observer

typedef struct NCTbl {
  Observation   *wildcard;  /* Save a notification */ that has neither notification name nor object passed in
 MapTable       nameless;   /* Save the notification */ with no notification name passed in
 MapTable       named; /* Save the notification with the name passed in
} NCTable;
Copy the code

NamedTable represents notification of incoming name

Defined Table refers to notices without a name and will not be described herein

NamedTable

NamedTable stores the specific form of data

First, the entire namedTable is a Map, and the name of each notification is used as the key. For example, study in the example above is a notification name, which is also a key. Its corresponding value is also a Map, and its key is the object of the notification. Which is School in the code below.

NSNotification notificationWithName:"study" object:School];
Copy the code

The corresponding value of each object is a linked list. Its implementation is as follows. In oc, if you call a method, you send a message to an object, so the object is an observer, and the method is a selector, so you call objc_msgSend, and you pass in the object and the method, and you send a call message to the object.

typedef struct  Obs {
  id        observer;   /* Save the object that receives the message */
  SEL       selector;   /* Save the SEL*/ passed in during the registration notification
  struct Obs    *next;      /* Save the next observer registered with the same notification */
  struct NCTbl  *link;  /* Save the Table*/
} Observation;
Copy the code

Therefore, if the object is not registered in the callback queue when it is released, then the callback queue will send a method call message to the released object after the new notification triggers the callback queue, causing an exception.

Therefore, when the registered listener is released, it is necessary to remove the registration from the notification callback queue

- (void) dealloc {
    NSLog(@" Object Destruction");
    [[NSNotificationCenter defaultCenter] removeObserver:self];
}
Copy the code

In iOS 9.0 and above, the notification center holds observers. Weak references were changed from unsafe_unretained to weak references, even if the observer object was not removed when released, but because the notification center is a weak reference, the weak reference that held the observer will be removed after the observer is recovered. AddObserverForName: object: queue: usingBlock: due to strong references, so still need to manually release

Case analysis

Based on the above principle, it can be concluded as follows:

For case one: listening in class methods, processing in class methods

Since the observer registers a class object, it is ok to use a class method to process the notification message, because sending a method call message to a class object is essentially looking for the implementation method in its metaclass, since the class method is also in the metaclass. So you can find the completion call.

However, since the class object is created when it is first imported and exists throughout the lifetime of the program, the registered observer receives processing notification messages throughout the lifetime of the program.

Case two, case three

Whether the observer registers a class object or an instance object, it needs to specify a method in its own constructed class to handle the message. If the class object uses an instance method to handle the message, the method will not be found, and if the instance object uses a class method to handle the message, the method will not be found.

conclusion

When registering an observer, be careful to set the observation object (addObserver) and the handling method (@selector) to ensure that the method can be found on that object. Since the handling method is called at runtime, static analysis does not give an error. Be careful!

If there is anything wrong with this article, please let me know in the comments and I will correct it. Thanks!!