A Surprising Corner Case of Using NSNotification, NSOperationQueue and GCD

Without running the code, tell which runs first, block 1 or block 2?

// Dispatch from main thread.
dispatch_async(_backgroundQueue, ^{
    [[NSOperationQueue mainQueue] addOperationWithBlock:^{
        // block 1
        NSLog(@"First Operation");
    }];

    __block id observer = [[NSNotificationCenter defaultCenter] addObserverForName:@"MyNotif" object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) {
        // block 2
        NSLog(@"Receive Notif");
        [[NSNotificationCenter defaultCenter] removeObserver:observer];
    }];

    [[NSNotificationCenter defaultCenter] postNotificationName:@"MyNotif" object:self];
});

Without running the code, tell which runs first, block 1 or block 2? In case you don't notice the difference, this time I use dispatch_sync instead of dispatch_async.

// Dispatch from main thread.
dispatch_sync(_backgroundQueue, ^{
    [[NSOperationQueue mainQueue] addOperationWithBlock:^{
        // block 1
        NSLog(@"First Operation");
    }];

    __block id observer = [[NSNotificationCenter defaultCenter] addObserverForName:@"MyNotif" object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) {
        // block 2
        NSLog(@"Receive Notif");
        [[NSNotificationCenter defaultCenter] removeObserver:observer];
    }];

    [[NSNotificationCenter defaultCenter] postNotificationName:@"MyNotif" object:self];
});

It is totally OK if your answer is incorrect because the second case is really tricky. To reveal what's going on, I inserted several NSLogs into the second code. This time you are encouraged to run the code to see it.

// Dispatch from main thread.
dispatch_sync(_backgroundQueue, ^{
    NSLog(@"%@", [NSThread isMainThread] ? @"In Main Thread" : @"In Background Thread");
    [[NSOperationQueue mainQueue] addOperationWithBlock:^{
        // block 1
        NSLog(@"First Operation");
    }];

    __block id observer = [[NSNotificationCenter defaultCenter] addObserverForName:@"MyNotif" object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) {
        // block 2
        NSLog(@"Operations in Queue: %@", [[NSOperationQueue currentQueue] operations]);
        NSLog(@"Receive Notif");
        [[NSNotificationCenter defaultCenter] removeObserver:observer];
    }];

    [[NSNotificationCenter defaultCenter] postNotificationName:@"MyNotif" object:self];
});

If it is still unclear to you, I'll show you some relevant documents:

dispatch_sync
As an optimization, this function invokes the block on the current thread when possible.

addObserverForName:object:queue:usingBlock:
queue
The operation queue to which block should be added.
If you pass nil, the block is run synchronously on the posting thread.

OK. I have to say that the explanation of the queue parameter of addObserverForName:object:queue:usingBlock: is not very clear for this corner case. I'll revise it as:

queue
The operation queue to which block should be added.
If you pass nil, the target thread to run the block is the posting thread. If the target thread is the posting thread the block is run synchronously without being enqueued.