Reputation: 31
I've been reading apple documents and still there is a question, that a i can't find an answer for. I've got an UIManagedDocument object, which has two nested contexts - the child one on a main thread and a parent one on a private thread. And next, i've got a server side. So, when the data arrives from server i want to insert it into my managed document on a background thread.
Is it thread safe, to create an async queue, create there an NSManagedObjectContext and set as it's parent UIManagedDocument's child context which is created in a main thread?
dispatch_queue_t fetchQ = dispatch_queue_create("Data fetcher", NULL);
dispatch_async(fetchQ, ^{
//here goes some code for downloading data from the server
NSManagedObjectContext * backgroundContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
[backgroundContext setParentContext:self.eventDatabase.managedObjectContext]; // is this thread safe?
//some code for creating objects in backgroundContext
NSLog(@"inserting data in background thread");
});
dispatch_release(fetchQ);
In other words - is it thread safe to assign to a context that was created on a private thread parent, that was created on a main thread?
Upvotes: 3
Views: 852
Reputation: 1
No Andrew managedobjectcontext is not thread safe. To achieve what you want you have to create a child managedcontext, do your stuff and then save change on child and parent context. remember that saving only pushes changes up one level.
NSManagedObjectContext *addingContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
[addingContext setParentContext:[self.fetchedResultsController managedObjectContext]];
[addingContext performBlock:^{
// do your stuffs
[addingContext save:&error];
[parent performBlock:^{
[parent save:&parentError];
}];
}];
Upvotes: 0
Reputation: 28349
You are using the private concurrency type. That means, you should run the code on its own queue (via performBlock). So, if you want to do that, you should do it like this...
NSManagedObjectContext * backgroundContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
backgroundContext.parentContext = self.eventDatabase.managedDocument;
backgroundContext.performBlock:^{
//here goes some code for downloading data from the server
//some code for creating objects in backgroundContext
NSLog(@"inserting data in background thread");
// Calling save on the background context will push the changes up to the document.
NSError *error = nil;
[backgroundContext save:&error];
// Now, the changes will have been pushed into the MOC of the document, but
// the auto-save will not have fired. You must make this call to tell the document
// that it can save recent changes.
[self.eventDatabase updateChangeCount:UIDocumentChangeDone];
});
If you want to manage the queue yourself, you should probably use a confinement MOC, you should init with NSConfinementConcurrencyType, or with the standard init, because that's the default. Then, it would look like this...
dispatch_queue_t fetchQ = dispatch_queue_create("Data fetcher", NULL);
dispatch_async(fetchQ, ^{
backgroundContext.parentContext = self.eventDatabase.managedDocument;
//here goes some code for downloading data from the server
NSManagedObjectContext * backgroundContext = [[NSManagedObjectContext alloc] init];
// Everything else is as in the code above for the private MOC.
});
dispatch_release(fetchQ);
Upvotes: 4