(Turn) [iOS] NSNotificationCenter use

Reprinted from: http://www.jianshu.com/p/a4d519e4e0d5

 

Recently, in the process of making tablets, I found some very irregular codes. When I accidentally fixed the payment bug, I saw the code of other projects, and the place where the notification was used was not removed. I thought that the payment crash of my module was because the notification was not removed. And when I debug and read the specific code, I found that it has nothing to do with this. In my impression, I once encountered the problem of crashing because I did not remove the notification. So I was surprised, so I wrote a demo to study it, and at the same time, I talked about NSNotificationCenterthe correct posture to use.

NSNotificationCenter

There is no need to say more about this, it is a message notification mechanism, similar to broadcasting. The observer only needs to register the things of interest with the message center. When there is a place to send the message, the notification center will send it to the object that registered the message. This also plays a role in decoupling between multiple objects. Apple has encapsulated this NSNotificationCenterfor us, so that we can easily register and remove notifications. However, some people's posture is still a little problem, let's take a look at the correct posture!

of the correct postureremove

As long as it NSNotificationCenteris registered, removeit exist, which is a consensus of everyone. But when you use it, you find that it has not been removed in the middle, and it seems that it has not hung UIViewController! addObserverI think many people may have the same question as me, is it because of the use of ARC? Is it automatically nilset ? Or is there some magic way Apple uses to implement this class? Let's explore it step by step.

First of all, after the NSNotificationCentermiddle addObserver, the reference count of this object is not incremented by 1, so it just saves the address. To verify this operation, let's test the code.

A test class to register for notifications:

@implementation MRCObject

- (id)init
{
    if (self = [super init]) {
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(test) name:@"test" object:nil];
    }
    return self;
}

- (void)test
{
    NSLog(@"=================");
}

- (void)dealloc
{
    [super dealloc];
}

@end

This class is very simple, just register a notification for it when it is initialized. removeBut it doesn't operate when it is destroyed . After we create this object in VC, then destroy it, and finally send this notification:

- (void)viewDidLoad {
    [super viewDidLoad];

    MRCObject *obj = [[MRCObject alloc] init];
    [obj release];

    [[NSNotificationCenter defaultCenter] postNotificationName:@"test" object:nil];
}

After entering this vc, we found that it was hung up. . And the printed information is:

2015-01-19 22:49:06.655 测试[1158:286268] *** -[MRCObject test]: message sent to deallocated instance 0x17000e5b0

We can find that a message was sent to the wild pointer object, so it died. From this point of view, Apple's implementation is basically the same, only the address of an object is saved, and it is not set when it is destroyed nil.

This can be proved, addObserverafter, there must be removean operation.

Now we UIViewControllerregister the notification in , do not remove it, and see if it will hang up.

- (void)viewDidLoad {
    [super viewDidLoad];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(test) name:@"test" object:nil];
}

First use navigationControllerto enter this page, then popgo out. Finally click the button event that sends the notification:

- (void)didButtonClicked:(id)sender
{
    [[NSNotificationCenter defaultCenter] postNotificationName:@"test" object:nil];
}

No matter how you click the button, he just doesn't hang! Are you depressed now? We can look for it, there is no removeoperation in your code, but it NSNotificationCenterhas been removed there, otherwise there will definitely be a problem with the upper wild pointer. Looking at it, it can only mean that it was UIViewControllersecretly removed for us when it was destroyed.

So how do we prove it? Since we can't see the source code, we don't know if there is a call. At this time, we can start from this notification center! ! ! How to start? As long as I prove that UIViewControllerthe method is called when it is destroyed, I removecan prove our conjecture is correct! At this time, we need to use the feature of our powerful category . Let NSNotificationCenter's rewrite its - (void)removeObserver:(id)observermethod to add a class:

- (void)removeObserver:(id)observer
{
    NSLog(@"====%@ remove===", [observer class]);
}

So import this category in our VC, and popcome out and see what happens!

2015-01-19 22:59:00.580 测试[1181:288728] ====TestViewController remove===

how about it? Is it possible to prove that the system UIViewControllercalls this method when it is destroyed. (It is not recommended that you use the category method to override the original method during development. Because the category method has a higher priority, it may affect other places. This is only for debugging).

The above also reminds us that when you are not destroying, do not call [[NSNotificationCenter defaultCenter] removeObserver:self];this method directly, because you may remove the notification registered by the system .

Pay attention to repetition of correct postureaddObserver

In our development, we can often see such code:

- (void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(test) name:@"test" object:nil];
}

- (void)viewWillDisappear:(BOOL)animated
{
    [super viewWillDisappear:animated];
    [[NSNotificationCenter defaultCenter] removeObserver:self name:@"test" object:nil];
}

It is to register the notification when the page appears, and remove the notification when the page disappears. You should pay attention here. It must be in pairs. If you are only viewWillAppear 中 addObservernot viewWillDisappear 中 removeObserverthere, your method will be called multiple times when the message occurs. This must be kept in mind.

Multi-threaded notification of correct posture

First look at Apple's official instructions:

Regular notification centers deliver notifications on the thread in which the notification was posted. Distributed notification centers deliver notifications on the main thread. At times, you may require notifications to be delivered on a particular thread that is determined by you instead of 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, you would like to receive the notifications in the background thread instead of the main thread. In these cases, you must capture the notifications as they are delivered on the default thread and redirect them to the appropriate thread.

The meaning is very simple, NSNotificationCenterthe receiving thread of the message is based on the thread that sent the message. That is, it is synchronous. Therefore, sometimes, the message you send may not be in the main thread, and everyone knows that the operation UI must be in the main thread, otherwise it will not respond. So, when you receive a message notification, pay attention to the thread you want to execute. Below is a sample code

//接受消息通知的回调
- (void)test
{
    if ([[NSThread currentThread] isMainThread]) {
        NSLog(@"main");
    } else {
        NSLog(@"not main");
    }
    dispatch_async(dispatch_get_main_queue(), ^{
        //do your UI
    });

}

//发送消息的线程
- (void)sendNotification
{
    dispatch_queue_t defaultQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_async(defaultQueue, ^{
        [[NSNotificationCenter defaultCenter] postNotificationName:@"test" object:nil];
    });
}

Summarize

There are almost so many knowledge points for notifications that are usually used. I hope everyone has to help. In the end, code must develop good habits, and what should be removed should be removed.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=326260176&siteId=291194637