【问题标题】:NSFetchedResultsControllers in different view controllers updating each other?不同视图控制器中的 NSFetchedResultsController 相互更新?
【发布时间】:2014-01-05 22:29:05
【问题描述】:

好的,我对这个问题越来越笼统了,因为我注意到我的应用程序因此而出现了一些滞后。我注意到重新排序的问题,但它也发生在其他地方。我有一个 CoreDataViewController 类,它是我所有的表视图控制器的子类。在这个类中,我基本上拥有所有 NSFetchedResultsController 委托方法,就像它们在苹果文档中一样。

然后我试图找出这个 NSFetchedResultsController 注意到更改的频率,以找出我的时间滞后在哪里:

- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller
{
    if(self.suspendAutomaticTrackingOfChangesInManagedObjectContext) return;
    NSLog(@"ControllerDidChangeContent");
    TICK;
    [self.tableView endUpdates];
    TOCK;
}

例如在我的视图控制器 A 中,我有这个获取请求(从 viewDidLoad 调用):

- (void)setupFetchedResultsController
{
    //NSError *error = nil;
    NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"SpendingCategory"];
    request.sortDescriptors = [NSArray arrayWithObject:[NSSortDescriptor sortDescriptorWithKey:@"position" ascending:YES]];
    //[self.mainCategory.managedObjectContext executeFetchRequest:request error:&error];
    request.predicate = [NSPredicate predicateWithFormat:@"belongsToMainCategory = %@", self.mainCategory];

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

如果我在这个视图控制器中更改我的对象的属性,我的日志只会打印一次注释“ControllerDidChangeContent”。并且和预期的一样快。我的意思实际上只是一个简单的属性更改,只是更改一些数字或字符串等,例如:

spendingCategory.name = @"Hello world";

但是,如果我已经访问了另一个在 viewDidLoad 中设置其 NSFetchedResultsController 的视图控制器,我的日志将被打印两次。这是第二个 NSFetchedResultsController:

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

    NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"SpendingCategory"];

    NSSortDescriptor *mainCatPosition = [[NSSortDescriptor alloc]
                            initWithKey:@"belongsToMainCategory.position" ascending:YES];
    NSSortDescriptor *spendingCatPosition = [[NSSortDescriptor alloc]
                            initWithKey:@"position" ascending:YES];

    request.sortDescriptors = [NSArray arrayWithObjects:mainCatPosition,spendingCatPosition,nil];
    request.predicate = [NSPredicate predicateWithFormat:@"liveBudget = %@", [NSNumber numberWithBool:YES]];

    self.fetchedResultsController = [[NSFetchedResultsController alloc]initWithFetchRequest:request
                                                                       managedObjectContext:self.managedObjectContext
                                                                         sectionNameKeyPath:@"belongsToMainCategory.position"
                                                                                  cacheName:@"LiveBudget"];

}

我之前提到的简单属性更改需要更长的时间。那是因为现在我的日志打印了两次(!)ControllerDidChangeContent。第一个 TICK-TOCK 仍然和以前一样快,但第二个超过一秒。我猜这是因为我有两个 NSFetchedResultsController 正在监视同一个实体。

  1. 问题: 我还是不太明白他们为什么会互相影响?我的意思是,我在一个视图控制器中更新了一个属性,所以另一个当然应该注意到这个变化,但是为什么触发了两个 didChangeContent 呢?

  2. 问题: 我怎样才能避免这种情况?或者我该如何改进?

【问题讨论】:

  • 每个VC收到一个回调?或者收到重复的回调?表格视图单元格包含什么?您将委托设置在哪里?
  • 每个 VC 在他们的视图中设置了 FRC 后都会收到两个回调。表格视图单元仅包含它们的所有属性(3 个 NSNumber,2 个字符串)。并且在视图中设置了委托也确实加载了

标签: ios objective-c core-data nsfetchedresultscontroller


【解决方案1】:

我认为最好在转向此解决方案之前检查 1[sec] 停止的原因。
我自己,在同一上下文中使用多个 FRC 时,我没有经历过这种延迟,所以我的猜测是问题出在其他地方。

至于你的问题:

  1. FRC 正在侦听“NSManagedObjectContextObjectsDidChangeNotification”,因此所有 FRC 在同一对象上的同一上下文中侦听将在这些对象发生更改时触发其委托的方法。因此,很清楚为什么您的日志被打印了两次(2 个不同的 FRC 更改了它们的内容)

  2. 1[sec] 阻塞主线程的时间很长,应该解决(检查仪器可以做什么)。正如我所提到的,在相同的上下文中使用 FRC 时无法避免这种情况,但是您可以为每个 VC 创建一个子上下文,并使 FRC 仅侦听该子上下文,然后更改将只对其他人可见FRC 当您保存上下文链时。只要您的 VC 堆栈不是很深,这应该不是一个真正的问题。在任何情况下,您都希望您的其他 VC 在更新 tableview 以提供流畅的用户体验时获得更新,因此如果更新时间过长,您应该了解原因。

【讨论】:

  • 感谢您的评论。如果我创建一个子上下文并保存它,我的另一个上下文会看到更改,即使没有再次执行获取?所以我可以让 VC 1 中的子上下文更改所有内容,保存上下文,然后 VC 2 立即显示更改?再次检查后,这个问题主要出现在重新排序,我自己更改属性并停止 FRC 检查更改,直到我完成。在更改了所有内容后,我保存了上下文并让他再次聆听。 ([fetchedResult setValue:[NSNumber numberWithInt:i++] forKey:@"position"];)
  • @MichiZH 保存子上下文会将更改传播到父上下文,这反过来又会转化为父上下文中的更改,并且侦听父上下文的 FRC 不需要重新获取对象(执行提取)。如果您的设置是 VC1(parent)->VC2(child) 并且您在 child 中进行了更改,那么当您保存子上下文时,父上下文将获得更改并更新 VC1。在 FRC 委托方法中更改对象是有问题的,因为更改将在下一个运行循环中再次调用这些方法。
  • 好吧,不,我正在更改表委托方法中的顺序,只是停止获取的结果控制器监听。 stackoverflow.com/questions/20935311/…。是的,当然有多个对象已更改,但控制器已更改被发送两次,用于机器人 FRC 侦听。而这需要时间。我正在考虑实现父子关系。
  • 我在另一种情况下解决了这个问题:由于具有相同获取的两个视图控制器之一仅用于显示,另一个主要用于编辑,我刚刚删除了所有委托方法显示视图控制器,并且总是在 viewWillAppear 中进行新的获取。到目前为止,我对此解决方案没有性能问题。或者这是一个糟糕的解决方案?
  • 您的重新排序方法非常低效,一个一个地出错(第一次运行非常慢)。其次,您忽略了 Marcus S. Zarra 关于更新 FRC 委托方法中的对象的建议。您的设置似乎很复杂。在每个 viewWillAppear 上执行提取和重建表视图也可能很复杂且效率低下。
猜你喜欢
  • 1970-01-01
  • 2014-01-22
  • 2012-12-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-07-08
相关资源
最近更新 更多