NSFetchedResultsController忽略fetchLimit?

时间:2011-02-01 02:16:42

标签: iphone ios core-data ios4 nsfetchedresultscontroller

我有一个NSFetchedResultsController来更新带有Core Data内容的UITableView。这是相当标准的东西,我相信你们已经多次见过但是我遇到了轻微的问题。首先,这是我的代码:

NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];

 NSEntityDescription *entity = [NSEntityDescription entityForName:@"Article" inManagedObjectContext:self.managedObjectContext];

 [fetchRequest setEntity:entity];

 [fetchRequest setFetchLimit:20];

 NSPredicate *predicate = [NSPredicate predicateWithFormat:@"(folder.hidden == NO)"];
 [fetchRequest setPredicate:predicate];

 NSSortDescriptor *sort1 = [NSSortDescriptor sortDescriptorWithKey:@"sortDate" ascending:NO];
 [fetchRequest setSortDescriptors:[NSArray arrayWithObjects:sort1, nil]];

 NSFetchedResultsController *controller = [[NSFetchedResultsController alloc]
         initWithFetchRequest:fetchRequest
         managedObjectContext:self.managedObjectContext
         sectionNameKeyPath:nil
         cacheName:nil];
 [fetchRequest release];

 controller.delegate = self;

 self.fetchedResultsController = controller;

 [controller release];

 NSError *error = nil;
 [self.fetchedResultsController performFetch:&error];
 if (error) {
  // TODO send error notification
  NSLog(@"%@", [error localizedDescription]);
 }

问题在于,商店最初没有实体,因为它从Web服务下载和同步。会发生什么是NSFetchedResultsController用来自商店的超过150行实体填充表,这是webservice返回的数量。但我设置了一个20的获取限制,它似乎忽略了。但是,如果我关闭应用程序并再次使用商店中已有的数据,它可以正常工作。我是我的代表,我这样做:

#pragma mark -
#pragma mark NSFetchedResultsControllerDelegate methods

- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller {
 [self.tableView beginUpdates];
}


- (void)controller:(NSFetchedResultsController *)controller didChangeSection:(id  <NSFetchedResultsSectionInfo>)sectionInfo
 atIndex:(NSUInteger)sectionIndex forChangeType:(NSFetchedResultsChangeType)type {

switch(type) {
    case NSFetchedResultsChangeInsert:
        [self.tableView insertSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade];
        break;

    case NSFetchedResultsChangeDelete:
        [self.tableView deleteSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade];
        break;
    }
}


- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject
atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type newIndexPath:(NSIndexPath *)newIndexPath {

UITableView *tableView = self.tableView;

switch(type) {

    case NSFetchedResultsChangeInsert:
        [tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
        break;

    case NSFetchedResultsChangeDelete:
        [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
        break;

    case NSFetchedResultsChangeUpdate:
        [self configureCell:[tableView cellForRowAtIndexPath:indexPath] atIndexPath:indexPath];
        break;

    case NSFetchedResultsChangeMove:
        [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
        [tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
        break;
    }
}


- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller {
 [self.tableView endUpdates]; 
}

Apple的开发文档中有哪些复制粘贴,任何想法都在进行?

7 个答案:

答案 0 :(得分:12)

我知道这是一个老问题,但我有一个解决方案:

由于NSFetchedResultsController中的某个已知错误不符合fetchlimit的{​​{1}},因此您必须手动处理NSFetchRequest内的记录限制和UITableViewDataSource方法。

<强>的tableView:numberOfRowsInSection:

NSFetchedResultsControllerDelegate

<强>控制器:didChangeObject:atIndexPath:forChangeType:newIndexPath:

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {

    id <NSFetchedResultsSectionInfo> sectionInfo = [[self.fetchedResultsController sections] objectAtIndex:section];

    NSInteger numRows = [sectionInfo numberOfObjects];

    if (numRows > self.fetchedResultsController.fetchRequest.fetchLimit) {

        numRows = self.fetchedResultsController.fetchRequest.fetchLimit;
    }

    return numRows;
}

答案 1 :(得分:5)

这是一个老问题,但我自己也遇到了它(在iOS 5中)。我想你正在遇到这里描述的错误:https://devforums.apple.com/message/279576#279576

该线程根据您是否具有sectionNameKeyPath提供解决方案。由于我(像你一样)没有,答案是将tableview与fetchedResultsController分离。例如,而不是使用它来确定行数:

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section 
{
        return [[[self.fetchedResultsController sections] objectAtIndex:0] numberOfObjects];

只需返回您的期望:

    - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section 
{
        return fetchLimit;

controller:didChangeObject中,只有在newIndexPath位于fetchLimit中时才插入新对象。

答案 2 :(得分:2)

在某些情况下,这些仍会崩溃,例如几次插入,或超出限制,...... 您必须将所有更改保存到4组,并计算另外4个数组并在-[UITableView endUpdates]之前删除/更新/插入tableView

有些事情(假设只有一个部分):

NSUInteger limit = controller.fetchRequest.fetchLimit;
NSUInteger current = <current section objects count>;
NSMutableArray *inserts = [NSMutableArray array];
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"row < %d", limit];

if (insertedIndexPaths.count) {
    NSUInteger deletedCount = 0;
    for (NSIndexPath *indexPath in insertedIndexPaths) {
        if (indexPath.row >= limit) continue;
            current++;
            if (current > limit) {
                deletedCount++;
                current--;
                [deletedIndexPaths addObject:[NSIndexPath indexPathForRow:limit - deletedCount inSection:indexPath.section]];
            }
            [inserts addObject:indexPath];
    }
}
if (movedIndexPaths.count) {
    for (NSIndexPath *indexPath in movedIndexPaths) {
        if (indexPath.row >= limit) {
            [updatedIndexPaths addObject:[NSIndexPath indexPathForRow:limit - 1 inSection:indexPath.section]];
        } else {
            [inserts addObject:indexPath];
        }
}
}
[updatedIndexPaths minusSet:deletedIndexPaths];
[deletedIndexPaths filterUsingPredicate:predicate];
[updatedIndexPaths filterUsingPredicate:predicate];
[_tableView insertRowsAtIndexPaths:inserts withRowAnimation:UITableViewRowAnimationFade];
[_tableView reloadRowsAtIndexPaths:[updatedIndexPaths allObjects] withRowAnimation:UITableViewRowAnimationNone];
[_tableView deleteRowsAtIndexPaths:[deletedIndexPaths allObjects] withRowAnimation:UITableViewRowAnimationFade];

[_tableView endUpdates];
deletedIndexPaths = nil;
insertedIndexPaths = nil;
updatedIndexPaths = nil;

答案 3 :(得分:1)

你遇到的问题是你在加载fetchedResultsController之前调用了全部数据,所以它显示你需要做的就是加载所有信息,然后调用fetchedResultsController

实施例

- (void)viewDidLoad {
    [super viewDidLoad];

    // Loading Articles to CoreData
    [self loadArticle];
}

- (void)ArticleDidLoadSuccessfully:(NSNotification *)notification {
    NSError *error;
    if (![[self fetchedResultsController] performFetch:&error]) {
        // Update to handle the error appropriately.
        NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
        abort();  // Fail
    }
    [tableView reloadData];
}   

答案 4 :(得分:1)

来自apple doc:https://developer.apple.com/reference/coredata/nsfetchrequest/1506622-fetchlimit

  

如果设置了一个提取限制,那么框架会尽最大努力,但不能保证提高效率。对于除SQL存储之外的每个对象存储,使用提取限制执行的提取请求实际上只执行无限提取并丢弃未提取的行。

答案 5 :(得分:1)

我于2014年在iOS 6/7上向Apple提交了一个关于此问题的错误报告。正如许多其他人所指出的那样,它仍然是iOS 9和10上的一个错误。我的原始错误报告仍然是开放的,没有来自Apple的反馈。 Here is an OpenRadar copy of that bug report.

这是我成功使用的修复程序,但它会被多次调用。请谨慎使用。

 ori.translateZ( - 100);

答案 6 :(得分:0)

这是我的诀窍:

我在保存&#39;之后设置了NSFetchedResultsController的委托。调用NSManagedObjectContext实例上的方法。

  1. 使用名称在UIViewController上设置观察者:例如。 &#39;同步&#39;
  2. 保存上下文后,发布包含该名称的通知:&#39;同步&#39;并触发一个设置委托
  3. 的函数(在你的viewcontroller中)

    PS。如果你不再需要它,请记得删除那个观察者