Ensure a message sent to an object from another thread is received after NSManagedObjectContextObjectsDidChangeNotification is received, not before

StackOverflow https://stackoverflow.com/questions/19059228

Question

My app has a typical Core Data backend with parent and child NSManagedObjectContext objects.

The parent NSManagedObjectContext, moc1, has NSMainQueueConcurrencyType.

The child NSManagedObjectContext, moc2, has NSPrivateQueueConcurrencyType.

I also have an object X that observes NSManagedObjectContextObjectsDidChangeNotification of moc1. This notification necessarily arrives on the main thread.

Question: say changes are made to moc2 on a background queue and then [moc2 save:] is called. How do you then send a message to object X on the main thread and guarantee that it is received after NSManagedObjectContextObjectsDidChangeNotification was received, not before? Is it sufficient to call:

[moc2 save:NULL];  // causes X to get NSManagedObjectContextObjectsDidChangeNotification for moc1...eventually
dispatch_async(dispatch_get_main_queue(), ^{
    [X iHopeYouGetThisAfterYouGotTheNotification];
};
Was it helpful?

Solution

Even if you were to observe empirically that such a block were executed after the notification, to rely on that would be to assume undocumented implementation details. What is safe to rely on is that NSNotificationCenter sends its notifications synchronously (barring one specific case discussed below), so by adding another observer, and dispatching the block from there, you can be safer.

You might do this like (assuming ARC):

__block id token = [[NSNotificationCenter defaultCenter] addObserverForName:NSManagedObjectContextObjectsDidChangeNotification object: moc1 queue: nil usingBlock:^(NSNotification *note) {
    if (token)
    {
        id copyOfToken = token; // so it'll be const-copied into the next block, despite token being nilled out
        dispatch_async(dispatch_get_main_queue(), ^{
            [X iHopeYouGetThisAfterYouGotTheNotification];
            [[NSNotificationCenter defaultCenter] removeObserver: copyOfToken];
        });
        token = nil;
    }
}];

Since that notification will be delivered synchronously on the posting queue, you can be assured that -iHopeYouGetThisAfterYouGotTheNotification will happen afterwards, regardless of the thread posting the notification.

Note that if X were receiving the notification asynchronously (say, by subscribing to the notification using -addObserverForName:object:queue:usingBlock: and having passed [NSOperationQueue mainQueue]) then this would not be guaranteed to work, but that seems like an unlikely (and avoidable) arrangement.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top