iOS:NSFetchedResultsController,ControllerDidChangeContent在重新排序后执行两次 [英] iOS: NSFetchedResultsController, ControllerDidChangeContent executed twice after reordering

查看:192
本文介绍了iOS:NSFetchedResultsController,ControllerDidChangeContent在重新排序后执行两次的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在类似的线程之前开始,但我现在知道问题在哪里,所以我缩小它为你:



我有两个视图控制器。第一个是 MainCategoriesViewController ,第二个是 NetIncomeViewController 。这两个都是我的 CoreDataViewController 的孩子。在 CoreDataViewController 中,我基本上只是实现了 NSFetchedResultsController 委托类的所有委托方法,



确定我的 MainCategoriesViewController 抓取请求是设置像这样,从 viewDidLoad:

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

NSFetchRequest * request = [NSFetchRequest fetchRequestWithEntityName:@MainCategory];
request.sortDescriptors = [NSArray arrayWithObject:[NSSortDescriptor sortDescriptorWithKey:@positionascending: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;

//只允许数组的可变拷贝,而不是
中的对象(引用)。NSMutableArray * fetchedResults = [[self.fetchedResultsController fetchedObjects] mutableCopy];

//抓取我们移动的项目
NSManagedObject * resultToMove = [self.fetchedResultsController objectAtIndexPath:sourceIndexPath];

//删除我们从数组中移动的对象。
[fetchedResults removeObject:resultToMove];
//现在重新插入到目的地。
[fetchedResults insertObject:resultToMove atIndex:[destinationIndexPath row]];

//所有对象现在都处于正确的顺序。通过遍历数组更新每个
//对象的displayOrder字段。
int i = 1;
for(MainCategory * fetchedResult in fetchedResults)
{
fetchedResult.position = [NSNumber numberWithInt:i ++];
}

self.suspendAutomaticTrackingOfChangesInManagedObjectContext = NO;
}

现在重新排序非常快。但是,如果你第一次切换到 NetIncomeViewController (执行EXACT同样的提取,但是使用不同的缓存名称),则重新排序 MainCategoriesViewControlle r变慢。我已经跟踪了,我现在知道为什么。因为 self.suspendAutomaticTrackingOfChangesInManagedObjectContext 设置为NO,所有 NSFetchedResultsController 委托方法执行TWICE!第一次,它们是第一次访问 NetIncomeViewController 之前的速度,第二次是非常慢。



为什么现在执行两次?因为他们从同一个实体获取?为什么这甚至有效果?我能做什么反对呢?

解决方案

(与其他问题相同)



在Instruments中运行时间分析器?如果是这样,那会告诉你为什么它很慢。时间分析器是您的朋友。



其次,在委托方法中设置断点。看看堆栈跟踪,这将显示什么是引起事情的火。



说到这里,你是变异的对象在 NSFetchedResultsController ,而您在 NSFetchedResultsController 的回调方法。



NSFetchedResultsController

/ code>对数据中的更改敏感。它不关心什么只改变你改变了什么。



如果您更改了 NSFetchedResultsController

的对象, code>正在监视中,而在代理中,你可以很容易陷入一个无尽的循环。唯一的原因是只有发射两次,是在第二次运行时,您将订单设置为已经发生的订单。



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


Have started before a similar thread, but I now know where the problem lies, so I'm narrowing it down for you:

I have two view controllers. First one is called MainCategoriesViewController, 2nd one is NetIncomeViewController. Both are child of my CoreDataViewController. In the CoreDataViewController I basically just have implemented all delegate methods of the NSFetchedResultsController delegate class with a property which is set if user changes occur (like reordering) and the controller shouldn't track these changes.

Ok my MainCategoriesViewController fetch request is setup like this and called from 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"];
}

The reordering is done like this:

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

Now the reordering is very fast. However if you switch for the first time to the NetIncomeViewController (doing the EXACT same fetch, however with a different cache name) the reordering in the MainCategoriesViewController gets slow. I've tracked it down and I know now why. Because after self.suspendAutomaticTrackingOfChangesInManagedObjectContext is set to NO, all NSFetchedResultsController delegate methods are executed TWICE! The first time they are as fast as before visiting the NetIncomeViewController for the first time, and the 2nd one is very slow.

Why is this now executed twice? Because they fetch from the same entity? Why does this even have an effect? And what can I do against it?

解决方案

(Same Answer from your other question)

Are you running the time profiler in Instruments? If so, that will tell you why it is slow. Time Profiler is your friend.

Second, put a break point in the delegate methods. Look at the stack trace, that will show you what is causing things to fire.

Having said that, you are mutating the objects in your NSFetchedResultsController while you are in a callback method of the NSFetchedResultsController.

That is bad.

The NSFetchedResultsController is sensitive to changes in the data. It doesn't care what you change only that you did change something. As soon as you change something, it fires the delegate methods to notify you of the change.

If you change an object that the NSFetchedResultsController is watching while in the delegate you could easily get into an endless loop. The only reason it is only firing twice is that on your second run through, you are setting the order to what it already was.

I would suggest moving your reordering logic to the -[UITableViewDataSource tableView: moveRowAtIndexPath: toIndexPath:] method.

这篇关于iOS:NSFetchedResultsController,ControllerDidChangeContent在重新排序后执行两次的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆