I am using 3 Managed Object Contexts Architecture (creating temporaryContext for background which parent is managedObjectContext - UI, and which has parent writerObjectContext which should write to database in background) and I have a problem with blocking UI when I updating objects. Example would be best. So I have thousands of points in my database and I am using NSFetchedResultsController
with tableView
for getting them. Here is my code:
- (void)viewDidLoad
{
[super viewDidLoad];
temporaryContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
temporaryContext.parentContext = [[CoreDataManager manager] managedObjectContext];
temporaryContext.undoManager = nil;
...
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:PositionCellIdentifier forIndexPath:indexPath];
[self configureCell:(PanelPositionCell *)cell atIndexPath:indexPath];
return cell;
}
- (void)configureCell:(PanelPositionCell *)cell atIndexPath:(NSIndexPath *)indexPath {
// Fetch Record
NSManagedObject *record = [self.fetchedResultsController objectAtIndexPath:indexPath];
OpenPositionCD *position = (OpenPositionCD *)record;
// Update Cell
[cell setValuesByOpenPositionCD:position];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
[self checkAddress:position];
});
}
- (void)checkAddress:(OpenPositionCD *)openPosition {
if (openPosition.latitude == 0 && openPosition.longitude == 0) {
return;
}
if ([openPosition hasAddress]) {
return;
}
CLLocation *location = [[CLLocation alloc]initWithLatitude:[openPosition.latitude doubleValue] longitude:[openPosition.longitude doubleValue]];
[[LocationManager manager] getPlacemarksForLocation:location withCompletion:^(NSArray *placemarks, NSError *error) {
if (!error) {
openPosition.address = placemarks[0];
NSError *error = nil;
if (![temporaryContext save:&error]) {
NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
}
}
}];
}
When I am scrolling to cells which don't have addresses the UI is freeze often which depends on how fast I am scrolling. So how can I fix it? I was trying with/without dispatch_async
and with/without temporaryContext performBlock
but looks nothing can help me. So thanks for any help.
I am adding initializing of contexts in CoreDataManager but I hope it's allright:
// Returns the managed object context for the application.
// If the context doesn't already exist, it is created and bound to the persistent store coordinator for the application.
- (NSManagedObjectContext *)managedObjectContext{
if (_managedObjectContext != nil) {
return _managedObjectContext;
}
_managedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
_managedObjectContext.parentContext = [self writerManagedObjectContext];
return _managedObjectContext;
}
// Writer context for database
- (NSManagedObjectContext *)writerManagedObjectContext{
if (_writerManagedObjectContext != nil) {
return _writerManagedObjectContext;
}
NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
if (coordinator != nil) {
_writerManagedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
[_writerManagedObjectContext setPersistentStoreCoordinator:coordinator];
}
return _writerManagedObjectContext;
}
You are using outdated APIs. The recommended way to use multiple contexts is not to assign the same persistent store coordinator to the child context, but instead assign it a parentContext
.
You probably want M. Zarra's setup with
WriterContext (background)
MainContext (main thread, parent is WriterContext)
WorkerContext (background, parent is MainContext, create and destroy as needed)
You would do the background work in a worker context and save
which would push the changes to the main context. You can save the main context when it is convenient, and the data store only gets hit in the background when the writer context is saving.
Finally, you are using the position object in a different thread. You need to wrap your calls into a worker context's performBlock
block to safely use these objects.