magicalpanda / MagicalRecord

Super Awesome Easy Fetching for Core Data!
Other
10.79k stars 1.79k forks source link

Main thread freezes after save #518

Closed xissburg closed 10 years ago

xissburg commented 11 years ago

I am using +[MagicalRecord saveWithBlock:completion:] to save in a background thread, however the UI still freezes right after this call. This is my code:

NSMutableArray *itemIds = [[NSMutableArray alloc] init]; // objectIDs to obtain the entities in the main thread
[MagicalRecord saveWithBlock:^(NSManagedObjectContext *localContext) {
    NSArray *items = [self updateItemsWithArrayOfDictionaries:itemsDictionaryArray context:localContext]; // creates items or updates if exists with new data
    for (OEItem *item in items) {
        [itemIds addObject:item.objectID];
    }
} completion:^(BOOL success, NSError *error) {
    if (!success && failure && error) {
        failure(error);
    }
    else if (completion) {
        NSMutableArray *items = [[NSMutableArray alloc] init];
        for (NSManagedObjectID *objectId in itemIds) {
            NSManagedObject *item = [[NSManagedObjectContext MR_contextForCurrentThread] objectWithID:objectId];
            [items addObject:item];
        }
        completion(items);
    }
}];

And below is the offending call stack in Instruments (Time Profiler). It looks like Core Data is performing some sort of fetch for weird reasons.

screen shot 2013-07-13 at 10 12 34 pm

I tried commenting parts of the code inside Magical Record and this only does not happen if I don't save (commented the call to -[NSManagedObjectContext save:] in NSManagedObjectContext+MagicalSaves.m line 64).

I am quite impressed that I didn't find much more info on this matter (possible bug). Am I doing something ridiculously wrong?

Thanks.

tonyarnold commented 11 years ago

It's entirely possible you're not doing anything wrong at all, but I'll need a bit more information to be able to help.

How big is the save operation you're performing? Even if you're saving in a background thread, at some point the main context will come into play (in this case, it looks like it's refreshing after a save has successfully occurred). Generally, all of your NSFetchedResultsControllers/NSArrayControllers and their ilk will be watching an NSManagedObjectContext that's running on the main thread — a huge save will result in a fair bit of information needing to be re-fetched. If this is the case, saving in batches can help — as can restricting the amount of information being fetched by your fetched results controllers/etc.

If it's not a big amount of data being saved, then we'll definitely have to do some digging.

xissburg commented 11 years ago

This problem is noticeable because I am adding and updating elements on a tableView while it scrolls. I conceptually split my table in chunks of 20 items, so after scrolling through every 20 items, I update the next (or previous if scrolling upwards) chunk of 20, or load more 20, adding them to the bottom. In each of these 'updates', I request the next 20 items to the server, and then I update these in the database (or create if they don't exist).

Hence, I always have 20 items in each save operation. Each of my entities has about 10-15 attributes. So, I don't think this a big save operation.

I noticed that it saves the main thread context because of the MRSaveParentContexts option. However, if I remove this option and not save the main context it still freezes. So the problem must be somewhere else.

I will try to setup a simple test project that showcases this issue.

Thanks.

tonyarnold commented 11 years ago

@xissburg that would be fantastic — I'd really like to smooth this out if it's on our side. There's a new approach to setting up your MOCs and PSCs that Apple recommended this year at WWDC, but that's a bigger change and I wouldn't expect that to hit until the next major MR release (3.0)

trunalb commented 11 years ago

I am also having the same problem. This thread on SO seems to suggest that this is a known bug in MR and is fixed in their experimental branch : http://bit.ly/19mS8PK. I will try out the experimental branch and see if things improve but thought of sharing here in case it fixes things for you too!

xissburg commented 11 years ago

Guys, I've created a sample project here https://github.com/xissburg/MagicalFreeze. This project pretty much summarizes exactly what I am doing in my original project.

@trunalb I've tried the feature/experiemental branch and the problem persists. You can try for yourself in the sample project I've uploaded.

You can see the table view freezing while scrolling after you go past items at indexes 20, 60, 100, 140… and so on. After scrolling all the way down, the database will stop updating because the random strings are generated only in the application start up. If you restart the app all the random strings are regenerated and so the database will be updated as you scroll and you'll be able to notice the lag again.

Thanks.

timewarrior commented 11 years ago

Me and Trunal are working on the same team. The experimental seems to have fixed the performance issues we were having. However we are planning to move to using coredata direclty, soon.