4

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;
}
Rob
  • 415,655
  • 72
  • 787
  • 1,044
fuzzygoat
  • 26,573
  • 48
  • 165
  • 294
  • Isn't it that performBlock: perform the operations in background thread. If so why are you again using the gcd block on the top of it. – Sandeep Nov 21 '12 at 23:24
  • in the example above the UIDocument (and therefore the managed object context) are created on the main thread. The code above executes on a background thread using dispatch_async. When using Core Data its important to note that the managed object context is not thread safe and can only be accessed on the thread where is was originally created. [moc performBlock:^{}]; executes the specified block on the thread where the moc was created, thus removing any potential threading issues. – fuzzygoat Nov 22 '12 at 12:56
  • Can you post the implementation of `createRodInContext:`. Within that method lies the key as to why it does or doesnt work – jackslash Nov 24 '12 at 23:56
  • Added implementation for createRodInContext: its pretty basic really, just a simple insertForEntityName: and then populating a few attributes from the supplied dictionary. Note: I am not at this stage using the returned Rod, I will probably remove it at some later date as I don't think I am going to need it. – fuzzygoat Nov 25 '12 at 16:54

1 Answers1

5

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.

Martin R
  • 529,903
  • 94
  • 1,240
  • 1,382
  • Thank you Martin, this is what I was thinking. I have been finding it hard to locate any information on this and just wanted to verify that I was doing things correctly. It seems that I am, and using performBlock: from the background thread is correct. Much appreciated ... – fuzzygoat Nov 25 '12 at 19:01