iOS:NSFetchedResultsController,ControllerDidChangeContent在重新排序后执行两次

iOS:NSFetchedResultsController,ControllerDidChangeContent在重新排序后执行两次,ios,objective-c,core-data,nsfetchedresultscontroller,Ios,Objective C,Core Data,Nsfetchedresultscontroller,在类似的线程之前已经开始,但我现在知道问题所在,所以我将为您缩小范围: 我有两个视图控制器。第一个称为MainCategoriesViewController,第二个称为NetIncomeViewController。两者都是myCoreDataViewController的子级。在CoreDataViewController中,我基本上只实现了nsfetchedresultscoontroller委托类的所有委托方法,并使用一个属性,该属性在用户发生更改(如重新排序)时设置,并且控制器不应跟踪

在类似的线程之前已经开始,但我现在知道问题所在,所以我将为您缩小范围:

我有两个视图控制器。第一个称为
MainCategoriesViewController
,第二个称为
NetIncomeViewController
。两者都是my
CoreDataViewController
的子级。在
CoreDataViewController
中,我基本上只实现了
nsfetchedresultscoontroller
委托类的所有委托方法,并使用一个属性,该属性在用户发生更改(如重新排序)时设置,并且控制器不应跟踪这些更改

Ok my
MainCategoriesViewController
fetch请求设置如下,并从
viewDidLoad:

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

    self.suspendAutomaticTrackingOfChangesInManagedObjectContext = NO;
}
现在重新排序非常快。但是,如果您第一次切换到
NetIncomeViewController
(执行完全相同的提取,但使用不同的缓存名称),则
main categoriesviewcontrolle
r中的重新排序会变慢。我已经找到了,现在我知道原因了。因为在将
self.suspendAutomaticTrackingOffChangesManagedObjectContext
设置为否之后,所有
NSFetchedResultsController
委托方法都会执行两次!第一次访问NetIncomeViewController的速度与第一次访问之前一样快,第二次非常慢

为什么现在要执行两次?因为它们是从同一个实体获取的?为什么这会产生影响?我该怎么对付它呢

(与您的另一个问题的答案相同)

你在用仪器运行时间分析器吗?如果是这样的话,这将告诉你为什么它很慢。时间分析器是你的朋友

其次,在委托方法中放置断点。看看堆栈跟踪,它会告诉你是什么引起了火灾

话虽如此,您在使用
NSFetchedResultsController
的回调方法时,正在对
NSFetchedResultsController
中的对象进行变异

那太糟糕了。

NSFetchedResultsController
对数据的更改很敏感。它不在乎你改变了什么,只在乎你确实改变了什么。一旦您更改了某些内容,它就会激发委托方法来通知您更改

如果在代理中更改
NSFetchedResultsController
正在监视的对象,则很容易陷入无休止的循环。它只发射两次的唯一原因是,在第二次运行时,您正在将顺序设置为它已经设置的顺序


我建议将您的重新排序逻辑移动到
-[UITableViewDataSource tableView:moveRowAtIndexPath:toIndexPath:
方法。

现在必须回答我自己的问题。通过两次编辑使其正常工作:

1) (如flexaddicted建议的)按如下顺序更改重新排序,以便现在触发其他委托方法:

int i = 1;
for (MainCategory *fetchedResult in fetchedResults)
{
    [fetchedResult setValue:[NSNumber numberWithInt:i++] forKey:@"position"];
}
确保我的managedObjectContext知道这一点:

    // Save
    [((AppDelegate *)[[UIApplication sharedApplication] delegate]) saveContext];
    // re-do the fetch so that the underlying cache of objects will be sorted
    // correctly
    [self.fetchedResultsController performFetch:nil];

2) 在我的第二个视图控制器(与MainCategoriesViewController具有完全相同的获取请求)中,我删除了所有委托方法,并在每次显示视图时(在ViewWillDisplay中)重新进行获取

您好,再次感谢您的回答。我很抱歉,但我真的不明白…我正在使用时间分析器,当然第一次比第二次快。如果我第一次做测试,需要0.05秒。访问NetIncomeViewController(并且基本上具有相同的获取请求)后,重新排序需要1。再次重复0.05秒,然后是1.5秒。你说我做什么坏事?很抱歉,我还是iOS新手,有点挣扎。特别是我的第二个视图控制器正在影响我的其他VC的FetchedResultsController,我不知道为什么..如果我查看堆栈跟踪,那么这两次委托方法都是由_managedObjectContextDidChange、UIApplicationMain和该main之前调用的。不知道如何处理此信息..您正在修改NSFetchedResultsController的委托方法中的托管对象。不要这样做,这会导致您的问题。是的,但如果不这样做,您将如何在NSFetchedResultsController中进行重新排序?还是我误解了什么?但奇怪的是,在我创建第二个NSFetchedResultsController之前,一切都按照我在回答中所说的那样正常工作,请在UITableViewDataSource方法中重新排序。