Background Thread Core Data Object Property Changes Doesn't Reflect on Ui

Multithreaded Core Data object has empty properties on main thread after saving on background thread

The problem, I think, is that Core Data is not thread safe. I know you know this - that, after all, is why you created a special background context - but it has very strict implications. You are saying:

dataManager.persistentContainer.performBackgroundTask { context in
context.mergePolicy = NSMergePolicy.mergeByPropertyObjectTrump
let user = self.userFactory.user(fromJSON: json, inContext: context)
do {
try context.save()
DispatchQueue.main.async {
print(user) // <--

You can't do that. You're still talking about the user you saved into the background context, but you've switched threads. No. You cannot talk about this user from two different threads.

Once you are on the main thread, the only way you may talk to Core Data is through the main context. You need to fetch the user out, by its id, from the main context. Now you can look to see how it is populated.

Is it possible to use background thread on NSFetchedResultsController for heavy read operation to ensure UI responsiveness?

I think it is possible that your problem is the assignment of the fetchedResultsControllerDelegate by the new controller. This seems to be inferable from where you encounter the multithreading violation.

From the code you provided, it is not clear what kind of object the delegate is, but presumably it manages the UI and thus is on the main thread. You can see who this can potentially lead to a threading problem.

Thanks for posting a link to this question from my answer here https://stackoverflow.com/a/14710475/427083. I still think that it should work for you if you assign the FRC as the datasource of you table once it is finished, and then reloadData.

On a side note: while I find this problem interesting and challenging from an engineering point of view, I doubt that it is good architecture to deal with millions of records directly on a mobile device.

Fetching Core Data objects in the background: objects not faulted

Why not take the easy route, since you are not saving anything new ?
Instead of creating an extra context for the background thread and working with IDs, use the main managedObjectContext in the background thread after locking it.

for example:

- (void) filterAllContactsIntoDictionary: (NSFetchedResultsController *) frc
{

if (filterMainQueue == nil) {
filterMainQueue = dispatch_queue_create("com.queue.FilterMainQueue", NULL);
}
dispatch_async(self.filterMainQueue, ^{

NSManagedObjectContext *context = ... // get the main context.
[context lock]; // lock the context.

// do something with the context as if it were on the main thread.

[context unlock]; // unlock the context.

dispatch_async(dispatch_get_main_queue(), ^{
CGPoint savedOffset = [self.tableView contentOffset];
[self.tableView reloadData];
[self.tableView setContentOffset:savedOffset];
});
});
}

This works for me when I call a method with performSelectorInBackground, so I guess it should work for GCD dispatch too.

Fetch related Core Data objects in background? (to prevent UI freeze)

Do you know for sure that it is your main thread that is causing the slowdown (sure sounds like it) - I'd use Instruments and "Time Profiler" to be sure, and there is also a way to turn on SQL debugging/timing too.

If it is your main thread, there are fantastic WWDC videos (take a look at 2010 too, not just 2011) on how to optimize Core Data.

I insert my CoreData in background Context and when I fetch, it doesn't show the data

The mistake you are making is to not test the many little pieces of this code which could be not working as expected. To troubleshoot something like this, you must start at the beginning and test one piece at a time. The first issue I see is confusion between managed object contexts…

  • It appears that the method saveManagedObjectContext is a getter for your main managed object context. In English, save is a verb, so that this method name implies that it is saving some managed object context. If I am correct, you should change the name saveManagedObjectContext to maybe mainMangaedObjectContext.

  • The statement _managedObjectContext = [appDel managedObjectContext] is quite nonstandard, assigning to an instance variable from what appears to be its getter. If, as usual, _managedObjectContext is the instance variable backing the property managedObjectContext, this statement has no effect.

  • In your first method you use [appDel saveManagedObjectContext], and in your second method you use [appDel managedObjectContext]. It looks like these should be the same context in order for your fetch to work. Are they?

UPDATE:

As you stated in your comment, that fixes the saving, but now you have a performance problem – saving to the persistent store on disk blocks the user interface, which is what your original code was trying to fix.

This is a solved problem. The solution is that your main-thread context, which interfaces the user, should be a child of your background-thread context which interfaces to the persistent store. It is explained nicely with code in this 2015 blog post by Marcus Zarra. For further reading, Chad Wilken has published a slight variation. Both are written in Objective-C for you :)

Changes not recognized my NSFetchedResultsController attached to background thread

The problem / limitation with FRC is that it only contains the entities for which it was setup. In this case:

self.fetchedResultsController = [Document MR_fetchAllSortedBy:@"timestamp"
ascending:NO
withPredicate:[NSPredicate predicateWithFormat:@"report.remoteId != nil && dirty == YES"]
groupBy:nil
delegate:self
inContext:_managedObjectContext];

the FRC will only contain Document entities. The initial fetch will work on the report relation. But if a report changes the FRC will not be updated, even though the predicate matches.

http://www.mlsite.net/blog/?p=825

and here

Changing a managed object property doesn't trigger NSFetchedResultsController to update the table view

Core Data locks in background threads

Not totally sure if this applies to you but I was getting similar errors. I solved them by

  1. Using NSPrivateQueueConcurrencyType not NSConfinementConcurrencyType to take processing off the main thread.

  2. Putting executeFetchRequest inside MOC's performBlockAndWait.

So in CoreDataHelper.m's fetchEntity method you would have something like this:

[context performBlockAndWait:^{
NSError *error = nil;
NSArray *entities = [context executeFetchRequest:request error:&error];
}];


Related Topics



Leave a reply



Submit