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
。两者都是myCoreDataViewController
的子级。在CoreDataViewController
中,我基本上只实现了nsfetchedresultscoontroller
委托类的所有委托方法,并使用一个属性,该属性在用户发生更改(如重新排序)时设置,并且控制器不应跟踪这些更改
Ok myMainCategoriesViewController
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方法中重新排序。