iOS: Saving managed object context takes over 1 second after a while

随声附和 提交于 2019-12-24 14:40:26

问题


Strange problem, and strangely only since yesterday. I have a fetched results controller in which you can reorder the rows without any problem. After a fresh start of the app, everything works fine and fast. However if you change from this tab bar to another tab bar and edit some random textfield (which isn't even linked to core data and doesn't trigger any save), the reordering is very slow. And I was able to narrow it down only to the save context. But now I have no clue where to look further. Any advice? Here's my reorder function:

- (void)tableView:(UITableView *)tableView
moveRowAtIndexPath:(NSIndexPath *)sourceIndexPath
      toIndexPath:(NSIndexPath *)destinationIndexPath;
{
    if (self.activeField && [self.activeField isFirstResponder]){
        [self.activeField resignFirstResponder];
    }

    self.suspendAutomaticTrackingOfChangesInManagedObjectContext = YES;

    //Makes only a mutable copy of the array, but NOT the objects (references) within
    NSMutableArray *fetchedResults = [[self.fetchedResultsController fetchedObjects] mutableCopy];

    // Grab the item we're moving
    NSManagedObject *resultToMove = [self.fetchedResultsController objectAtIndexPath:sourceIndexPath];

    // Remove the object we're moving from the array.
    [fetchedResults removeObject:resultToMove];
    // Now re-insert it at the destination.
    [fetchedResults insertObject:resultToMove atIndex:[destinationIndexPath row]];

    // All of the objects are now in their correct order. Update each
    // object's displayOrder field by iterating through the array.
    int i = 1;
    for (MainCategory *fetchedResult in fetchedResults)
    {
        fetchedResult.position = [NSNumber numberWithInt:i++];
    }

    // Save
    TICK;
    [((AppDelegate *)[[UIApplication sharedApplication] delegate]) saveContext];
    TOCK;

    self.suspendAutomaticTrackingOfChangesInManagedObjectContext = NO;
}

Yesterday I've only inserted these two methods (which cannot be the problem, because after commenting them so they don't trigger, the same problem remained):

-(void) registerForKeyboardNotificationsWithTabBarHeight:(CGFloat)tabbarHeight andHeaderHeight:(CGFloat)headerHeight andFooterHeight:(CGFloat)footerHeight  andBottomSpacingToTextFields:(CGFloat)spacingToTextFields
{
    self.tabbarHeight = tabbarHeight;
    self.footerHeight = footerHeight;
    self.headerHeight = headerHeight;
    self.spacingToTextFields = spacingToTextFields;

    // Register notification when the keyboard will be show
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(keyboardWillShow:)
                                                 name:UIKeyboardWillShowNotification
                                               object:nil];

    // Register notification when the keyboard will be hide
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(keyboardWillHide:)
                                                 name:UIKeyboardWillHideNotification
                                               object:nil];
}

- (void)dealloc
{
    [[NSNotificationCenter defaultCenter] removeObserver:self];
}

FetchedResultsController:

- (void)setupFetchedResultsController
{
    self.managedObjectContext = ((AppDelegate *)[[UIApplication sharedApplication] delegate]).managedObjectContext;

    NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"MainCategory"];
    request.sortDescriptors = [NSArray arrayWithObject:[NSSortDescriptor sortDescriptorWithKey:@"position" ascending:YES]];

    self.fetchedResultsController = [[NSFetchedResultsController alloc]initWithFetchRequest:request
                                                                       managedObjectContext:self.managedObjectContext
                                                                         sectionNameKeyPath:nil                                                                                  cacheName:@"MainCategoryCache"];
}

And the view will appear is also very harmless..

- (void)viewWillAppear:(BOOL)animated
{
    //Initialize database
    [super viewWillAppear:animated];

    [self displayBudget];

    /*
    This is only necessary since the sum of all categories is calculated and the FetchedResultsController
    doesn't get this since it's only monitoring a MainCategory and the changes occured in the Subcategories */
    [self.tableView reloadData];
}

I'm thankful for any clues what could be the problem :-(


回答1:


I would not recommend multi-threading in this situation. The issue is that your saves are being done at the wrong time. Moving them to a background queue is not going to solve the issue. You need to change your save behavior.

Writing to disk is expensive. There is no shortcut around it. Even if you move your saves into another context (there is an Apple example on this) they are still doing to take time and potentially block your UI (you cannot do a new fetch during a save). You need to change your save behavior.

You do not need to save every time a user moves a row. That is wasteful. You want to queue up your saves so that you are doing more each save. In addition, you should be saving when the user expects there to be a delay. When you push or pop a view might be a good time for a save. When your app goes into the background is a great time for a save.

Saving when the user wants smooth UI response is bad and causes a bad user experience. Look for opportunities where the user is expecting something to take time. Posting something to a server? Save at the same time.

Your NSFetchedResultsController, etc. will read unsaved data from your main NSManagedObjectContext just as it will saved data. You are not gaining anything on the UI by saving frequently.

Update 1

Step one in performance problems is to run your application in Instruments.

Measure the speed and then see what is costing you time. Time Profiler is your friend.




回答2:


Yes it will slow drawing your UI because you should't use saving and creating objects in main Queue; read this https://developer.apple.com/library/ios/documentation/cocoa/conceptual/CoreData/Articles/cdConcurrency.html

you should have another background thread and you should do inserting deleting stuff there and then you have to pass NSObjectID_s for newManagedObject_s and then get it in main thread:

NSManagedObjectContext * mainThreadContext;

NSManagedObject * object = [mainThreadContext objectWithId:objectID];



来源:https://stackoverflow.com/questions/20900310/ios-saving-managed-object-context-takes-over-1-second-after-a-while

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!