NSNotification vs. dispatch_get_main_queue

В отношении этот вопрос мне было интересно, существует ли общепринятая логика относительно использования NSNotification с наблюдателем в вашем основном потоке, а также с использованием GCD для отправлять задания из фоновой нити в основной поток?

Похоже, что при настройке уведомления-наблюдателя вы должны помнить о том, чтобы скрыть наблюдателя, когда ваше представление выгружается, но затем вы надежно игнорируете уведомление, где, когда отправка задания в основной поток может привести к выполнению блока, когда представление было выгружено.

Таким образом, мне кажется, что уведомления должны обеспечивать улучшенную стабильность приложения. Я предполагаю, что опция отправки обеспечивает лучшую производительность из того, что я читал о GCD?

UPDATE:

Я знаю, что уведомления и отправка могут работать вместе и в некоторых случаях должны использоваться вместе. Я пытаюсь выяснить, есть ли конкретные случаи, когда нужно/не следует использовать.

Пример: Почему я должен выбрать основной поток для запуска уведомления из отправленного блока, а не просто отправки функции приема в основной очереди? (Очевидно, что в двух случаях будут изменения в принимающей функции, но конечный результат будет таким же).

Ответ 1

NSNotificationCenter и gcd и dispatch_get_main_queue() служат для разных целей. Я не знаю, что "против" действительно применимо.

NSNotificationCenter обеспечивает способ устранения разрозненных частей вашего приложения. Например, пример кода kReachabilityChangedNotification в Apple Reachability отправляется в центр уведомлений при изменении состояния сетевой системы. И, в свою очередь, вы можете попросить центр уведомлений вызвать ваш селектор/вызов, чтобы вы могли ответить на такое событие. (Think Air Raid Siren)

gcd, с другой стороны, обеспечивает быстрый способ задания работы в указанной вами очереди. Он позволяет вам рассказать системе о точках, в которых ваш код может быть расчленен и обработан отдельно, чтобы использовать преимущества нескольких потоков и многоядерных процессоров.

Обычно (почти всегда) уведомления отображаются в потоке, на котором они размещаются. За исключением одного фрагмента API...

Один фрагмент API, где эти понятия пересекаются, NSNotificationCenter:

addObserverForName:object:queue:usingBlock:

Это, по сути, удобный метод обеспечения того, чтобы данное уведомление наблюдалось в данном потоке. Хотя параметр "usingBlock" дает, что за кулисами он использует gcd.

Вот пример его использования. Предположим, что где-то в моем коде есть NSTimer, вызывающий этот метод каждую секунду:

-(void)timerTimedOut:(NSTimer *)timer{
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        // Ha! Gotcha this is on a background thread.
        [[NSNotificationCenter defaultCenter] postNotificationName:backgroundColorIsGettingBoringNotification object:nil];
    });
}

Я хочу использовать backgroundColorIsGettingBoringNotification в качестве сигнала для меня, чтобы изменить цвет фона представления контроллера. Но он размещен на фоновом потоке. Хорошо, я могу использовать вышеупомянутый API, чтобы наблюдать это только на основном потоке. Примечание viewDidLoad в следующем коде:

@implementation NoWayWillMyBackgroundBeBoringViewController {
    id _observer;
}
-(void)observeHeyNotification:(NSNotification *)note{
    static NSArray *rainbow = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        rainbow = @[[UIColor redColor], [UIColor orangeColor], [UIColor yellowColor], [UIColor greenColor], [UIColor blueColor], [UIColor purpleColor]];
    });
    NSInteger colorIndex = [rainbow indexOfObject:self.view.backgroundColor];
    colorIndex++;
    if (colorIndex == rainbow.count) colorIndex = 0;
    self.view.backgroundColor = [rainbow objectAtIndex:colorIndex];
}
- (void)viewDidLoad{
    [super viewDidLoad];
    self.view.backgroundColor = [UIColor redColor];
    __weak PNE_ViewController *weakSelf = self;
    _observer = [[NSNotificationCenter defaultCenter] addObserverForName:backgroundColorIsGettingBoringNotification
                                                                  object:nil
                                                                   queue:[NSOperationQueue mainQueue]
                                                              usingBlock:^(NSNotification *note){
                                                                  [weakSelf observeHeyNotification:note];
                                                              }];
}
-(void)viewDidUnload{
    [super viewDidUnload];
    [[NSNotificationCenter defaultCenter] removeObserver:_observer];
}
-(void)dealloc{
    [[NSNotificationCenter defaultCenter] removeObserver:_observer];
}

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation{
    return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
@end

Основным преимуществом этого API, по-видимому, является то, что ваш наблюдательный блок будет вызван во время вызова postNotification.... Если вы использовали стандартный API и реализовали observeHeyNotification:, как показано ниже, не было бы никакой гарантии, сколько времени будет выполнено до того, как ваш блок отправки будет выполнен:

-(void)observeHeyNotification:(NSNotification *)note{
    dispatch_async(dispatch_get_main_queue(), ^{
        // Same stuff here...
    });
}

Конечно, в этом примере вы можете просто не размещать уведомление в фоновом потоке, но это может пригодиться, если вы используете фреймворк, который не дает никаких гарантий относительно того, в какой поток он будет отправлять уведомления.