【问题标题】:iOS: Saving managed object context takes over 1 second after a whileiOS:一段时间后保存托管对象上下文需要超过 1 秒
【发布时间】:2014-01-03 09:22:54
【问题描述】:

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

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

我很感谢任何线索可能是什么问题:-(

【问题讨论】:

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


    【解决方案1】:

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

    写入磁盘很昂贵。它周围没有捷径。即使您将保存移动到另一个上下文(有一个 Apple 示例),它们仍然会花费时间并可能会阻塞您的 UI(您不能在保存期间进行新的提取)。你需要改变你的保存行为。

    您不需要在每次用户移动一行时保存。那是浪费。您想将保存排​​队,以便每次保存都做更多。此外,当用户预计会有延迟时,您应该保存。当您推送或弹出视图时,可能是保存的好时机。当您的应用进入后台时是伟大保存的时间。

    在用户想要流畅的 UI 响应时保存是不好的,会导致不好的用户体验。寻找用户期望某事需要时间的机会。向服务器发布内容?同时保存。

    您的NSFetchedResultsController 等将从您的主NSManagedObjectContext 读取未保存的数据,就像它会保存数据一样。频繁保存不会在 UI 上获得任何好处。

    更新 1

    解决性能问题的第一步是在 Instruments 中运行您的应用程序。

    测量速度,然后看看是什么花费了您的时间。 Time Profiler 是您的朋友。

    【讨论】:

    • 保存类似于交易。因此,每当我有一组需要原子可见的更改时,我都会保存。但是,当您遇到性能问题时,这样的折衷方案是合适的。
    • 此外,当用户预计会有延迟时,您应该保存是一个非常好的建议。
    • 嗯,我的应用程序显然在其他地方有问题。即使在每行重新排序后删除保存并将其移动到其他地方后,重新排序后它仍然滞后,不知道为什么......
    【解决方案2】:

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

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

    NSManagedObjectContext * mainThreadContext;

    NSManagedObject * object = [mainThreadContext objectWithId:objectID];

    【讨论】:

    • 是的,我已经偶然发现了这个。但在其他一些主题或教程中,人们提到始终使用相同的托管对象上下文。这不是矛盾吗?所以我应该把所有的删除、添加实体和保存在单独的线程中?但是在主线程中访问托管对象的属性不是问题吗?
    • 不,你应该为每个 NSManagedObjectContext 使用一个 NSPersistantStoreCoordinator,没有很好的教程如何做到这一点,但是一旦我做了一些研究并编写了一些社交网络应用程序并且运行速度非常快并且用户数量很大当他没有连接时离线数据..并在两个不同的托管上下文之间同步数据:核心数据抛出一个 NSNotification 并使用应该捕获它并将更改合并到您的主队列托管上下文
    • 我不会推荐这个解决方案,正如我对这个问题的回答中所讨论的那样。添加线程和另一个上下文只会让事情变得更加复杂,而没有什么重大好处。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-05-25
    • 2021-11-14
    • 1970-01-01
    • 2012-12-29
    相关资源
    最近更新 更多