Monday, January 25, 2016

Follow up on 22k objects | iOS

Just recently had to work again on the project which was dealing with ~22k objects.

On iOS there is a problem with main thread deadlock, in case you need to merge large amount of objects from background managed object context to main managed object context, while fetchedResultsController is listening to the changes.

In such case I found a simple workaround. Detect merged items count and in case it is too large, simply notify fetchedResultsController to remove its delegate, until merge is done. Then simply reload tableView.
 
- (void)backgroundContextDidSave:(NSNotification *)notification
{
    if (![NSThread isMainThread])
    {
        [self performSelectorOnMainThread:@selector(backgroundContextDidSave:)
            withObject:notification waitUntilDone:NO];
        
        return;
 }
 
 
 int countOfUpdatedInsertedObj = MAX([[[notification userInfo] objectForKey:NSUpdatedObjectsKey] count],
        [[[notification userInfo] objectForKey:NSInsertedObjectsKey] count]);
    
    
    if(countOfUpdatedInsertedObj > 500) //In case we have big crap-load to save - this will disable fetchcontroller delegate.
    {
        [[NSNotificationCenter defaultCenter] postNotificationName:BackgroundContextWillStartMergingIntoMainContextNotification object:nil];
    }
 
 
    NSManagedObjectContext *managedObjectContext = [notification object];
    
    if (([managedObjectContext persistentStoreCoordinator] == AppDelegate.db.persistentStoreCoordinator) &&
        (managedObjectContext != AppDelegate.db.managedObjectContext))
    {
        for(NSManagedObject *object in [[notification userInfo] objectForKey:NSUpdatedObjectsKey])
        {
            [[AppDelegate.db.managedObjectContext objectWithID:object.objectID] willAccessValueForKey:nil];
        }

        for(NSManagedObject *object in [[notification userInfo] objectForKey:NSInsertedObjectsKey])
        {
            [[AppDelegate.db.managedObjectContext objectWithID:object.objectID] willAccessValueForKey:nil];
        }


        [AppDelegate.db.managedObjectContext mergeChangesFromContextDidSaveNotification:notification];
    }
 
    
    if(countOfUpdatedInsertedObj > 500) //This means we previously warned any active fetchedResultsController to remove its delegate. So we set it up again.
    {
        [[NSNotificationCenter defaultCenter] postNotificationName:BackgroundContextWasMergedIntoMainContextNotification object:nil];
    }
}

BackgroundContextWillStartMergingIntoMainContextNotification and BackgroundContextWasMergedIntoMainContextNotification were created just for this purpose.

No comments:

Post a Comment