Search code examples
iphoneobjective-ccocoa-touchgrand-central-dispatchuimanageddocument

Adding to UIManagedDocument from GCD background queue?


I am creating and adding a number of managed objects to Core Data from a background queue. My understanding was that I could not access the context from the background thread so I was using performBlock to schedule adding to Core Data back onto the same queue that the context was created on. This works just fine ...

My question is during testing I noticed that by removing [moc performBlock:^{ ... }]; the application still performs as expected (maybe even fractions of a second faster) Do I need the performBlock? I would assume I do and its just working (for now :) in a none-threadsafe fashion, I just wanted to check to make sure my understanding is not flawed.

dispatch_queue_t backgroundQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
dispatch_async(backgroundQueue, ^{ 
    // GET DATA
    // PROCESS DATA
    NSManagedObjectContext *context = [[self managedDocument] managedObjectContext];
    [moc performBlock:^{
          // ADD TO CORE DATA
          [Core createRodInContext:context withDictionary:fuelRodDictionary];
     }];

});

EDIT: Added implementation for createRodInContext ...

+ (Rod *)createRodInContext:(NSManagedObjectContext *)context withDictionary:(NSDictionary *)dictionary {

    // CREATE
    Rod *rod = [NSEntityDescription insertNewObjectForEntityForName:@"Rod" inManagedObjectContext:context];

    // POPULATE
    [neo setDataCode:[dictionary objectForKey:@"dataCode"]];
    [neo setDataName:[dictionary objectForKey:@"dataName"]];
    [neo setDataReference:[dictionary objectForKey:@"dataReference"]];
    ...
    return rod;
}

Solution

  • In the background thread you have to use [moc performBlock:^{ ... }] to insert (and populate) a managed object in the main managed object context.

    Omitting the performBlock means that you use the managed object context (which was created on the main thread) also in a different thread (which is associated with the background queue).

    This might work by chance, but as soon as the main thread accesses the MOC in the same moment as your background thread, the results are unpredictable because (as you already said), a MOC is not thread-safe.

    See also Concurrency Support for Managed Object Contexts in the Core Data Release Notes for OS X v10.7 and iOS 5.0:

    Confinement (NSConfinementConcurrencyType).

    This is the default. You promise that context will not be used by any thread other than the one on which you created it.

    But also for the other concurrency types (private queue, main queue), you always have to use performBlock (or performBlockAndWait) unless your code is already executing on the queue associated with the MOC.