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!