How to avoid the UI freeze while a Managed Object Context is saving?
Asked Answered
B

1

3

I want to implement a UI-responsive downloading and parsing of a large data set, saving it with Core Data.

My setup:

I display the downloaded content in a custom view controller. I don't use a NSFetchedResultsController.

There are 3 MOCs:

  1. masterMOC (responsible for saving to disk, NSPrivateQueueConcurrencyType)
  2. mainMOC (used by UI, NSMainQueueConcurrencyType, a child of the masterMOC)
  3. backgroundMOC (responsible for the import from JSON, created in a separate thread, a child of the masterMOC)

I am importing in batches - every 50 items I perform the MOC saving in the following way:

NSError *error;
[backgroundMOC save:&error];
NSManagedObjectContext *masterMOC = backgroundMOC.parentContext; //set during initialization               
[masterMOC performBlock:^{
    NSError *parentContextError = nil;
    [masterMOC save:&parentContextError];
}];

I expect the changes in the mainMOC to be made after the masterMOC is saved. If I try to access some relationship of a random managed object while the masterMOC is saving (saving takes some time), the UI hangs until the saving is completed.

Question: how to avoid the UI freeze while the masterMOC is saving?

Busty answered 15/1, 2013 at 15:7 Comment(4)
Rereading your question: Is the code snippet above performed on the main thread or in a separate one?Weekender
I don't see the advantage of using a block then. Why don't you put backroundMOC and masterMOC into the same thread?Weekender
What does take so long, anyway? Parsing or saving? Is your data just big or also complex? I guess the safest path should be atomizing the data sufficiently to make commits short enough.Weekender
I've implemented the approach described here: #10542597. I do saving of the masterMOC in a block because it was initialized with the NSPrivateQueueConcurrencyTypeBusty
W
0

Your problem probably is that the data store is blocking while you are writing to it. So, either make the data store non-blocking (this may or may not be possible in your case) or if not viable, make the accessor non-blocking. In the latter case the GUI will not hang, but it also will not update either until the result of the access comes back.

Weekender answered 15/1, 2013 at 15:41 Comment(6)
that sounds promising! But I cannot find any info on how any of these two cases are accomplished? Should I set the non-blocking somewhere in the configuration part of the addPersistentStoreWithType:configuration:URL:options:error for the NSPersistentStoreCoordinator?Busty
If I remember correctly, there is not much of a choice in Core Data. If you use, say, an SQLite store, you depend for example on whether SQLite was compiled with thread safety on, as well as how Core Data internally manages access to SQLite (i.e. do the threads share a file descriptor = blocking; a file descriptor per thread = non-blocking). I think it is not possible to tell Core Data to use a custom built SQLite though, so you depend on what Apple provides. I could be wrong, though. The same applies to the other data stores.Weekender
If it is an option, you could also make the chunks you write at once to the store smaller and issue the next commit only when the previous job is done. This way you could minimize the frozen UI time: The read is enqueued after a relatively small write, not a big one.Weekender
I tried the later option: before I had a hang of about 1min for 500 parsed items, now having the chunks of 50 items the UI hangs for about 5sec but 10 times, so it's hard to say which way is more annoying to the user :)Busty
thanks for the help anyway, and how could I achieve the second option, i.e. make the accessor non-blocking, is it any easier?Busty
Well, if you do it in chunks of 1 item, the UI should hang only about 100ms, right? This sounds already responsive. 2nd option: That largely depends on your codebase and how you access the elements. But the idea is, that the main thread makes a request to a separate thread and provides a callback that the other thread calls when the value has been read.Weekender

© 2022 - 2024 — McMap. All rights reserved.