iOS:保存托管对象上下文需要一段时间后超过1秒

时间:2014-01-03 09:22:55

标签: ios objective-c uitableview core-data nsfetchedresultscontroller

奇怪的问题,奇怪的是从昨天开始。我有一个获取结果控制器,您可以在其中重新排序行没有任何问题。在应用程序重新开始之后,一切都运行良好而快速。但是,如果您从此选项卡栏更改为另一个选项卡栏并编辑一些随机文本字段(甚至没有链接到核心数据并且不会触发任何保存),则重新排序非常慢。而我只能将其缩小到保存环境。但现在我不知道在哪里可以看得更远。有什么建议?这是我的重新排序功能:

- (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;
}

昨天我只插入了这两种方法(这不是问题,因为在评论它们之后它们不会触发,同样的问题仍然存在):

-(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"];
}

而且看来也很无害......

- (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];
}

我很感谢任何可能出现问题的线索: - (

2 个答案:

答案 0 :(得分:5)

在这种情况下,我不建议使用多线程。问题是您的保存是在错误的时间完成的。将它们移动到后台队列并不能解决问题。您需要更改保存行为。

写入磁盘非常昂贵。周围没有捷径。即使您将保存移动到另一个上下文(这里有一个Apple示例),他们仍然需要花时间并可能阻止您的UI(您在保存期间无法进行新的提取)。您需要更改保存行为。

每次用户移动行时都不需要保存。这很浪费。您希望将保存排队,以便每次保存都执行更多操作。此外,当用户期望有延迟时,您应该保存。推送或弹出视图时可能是保存的好时机。当您的应用进入后台时,很棒时间可以进行保存。

当用户想要流畅的UI响应时保存很差并导致糟糕的用户体验。寻找用户期待某些事情需要时间的机会。将内容发布到服务器?保存在同一时间。

您的NSFetchedResultsController等将从主NSManagedObjectContext读取未保存的数据,就像保存数据一样。通过经常保存,您无法在UI上获得任何东西。

更新1

性能问题的第一步是在仪器中运行您的应用程序。

测量速度,然后看看花费你时间的成本。 Time Profiler是你的朋友。

答案 1 :(得分:1)

是的,它会减慢绘制UI的速度,因为你不应该在主队列中使用保存和创建对象; 阅读此https://developer.apple.com/library/ios/documentation/cocoa/conceptual/CoreData/Articles/cdConcurrency.html

你应该有另一个后台线程,你应该在那里插入删除东西然后你必须传递NSObjectID_s for newManagedObject_s然后在主线程中获取它:

  

NSManagedObjectContext * mainThreadContext;

     

NSManagedObject * object = [mainThreadContext objectWithId:objectID];