2011-02-01 60 views
21

我有一个NSFetchedResultsController更新与核心数据的内容的UITableView。这是非常标准的东西,我相信你们都看过很多次,但是我遇到了轻微的问题。首先这里是我的代码:NSFetchedResultsController忽略fetchLimit?

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行实体的表格,这是web服务返回的次数。但是我设置了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]; 
} 

这是从苹果的开发文件,复制粘贴非常复制粘贴,任何想法是什么?

回答

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

听起来不错,我会给予一个镜头,让你知道会发生什么。 – marchinram 2011-02-03 05:56:21

+0

我不太确定这是否必要...是否有效? – 2011-03-02 23:34:50

+0

我想知道为什么人们仍然放弃();在代码中。应用程序将会被拒绝为该;-)正确的错误处理就足够了(或者只是使用NSAssert) – Lukasz 2015-04-08 11:18:07

5

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

该线程根据您是否拥有sectionNameKeyPath来提供解决方案。由于我(如你)没有,答案是从fetchedResultsController中解耦tableview。例如,而不是使用它来确定的行数:

- (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内。

11

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

由于在NSFetchedResultsController一个已知的bug,不兑现NSFetchRequestfetchlimit,你必须手动处理的限制记录在您的UITableViewDataSourceNSFetchedResultsControllerDelegate方法中。

的tableView:numberOfRowsInSection:

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

控制器:didChangeObject:atIndexPath:forChangeType:newIndexPath:

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

    switch(type) { 

     case NSFetchedResultsChangeInsert: 

      if ([self.tableView numberOfRowsInSection:0] == self.fetchedResultsController.fetchRequest.fetchLimit) { 
       //Determining which row to delete depends on your sort descriptors 
       [self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:[NSIndexPath indexPathForRow:self.fetchedResultsController.fetchRequest.fetchLimit - 1 inSection:0]] withRowAnimation:UITableViewRowAnimationFade]; 

      } 

      [self.tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] 
          withRowAnimation:UITableViewRowAnimationFade]; 
     break; 
     ... 
    } 
} 
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; 
1

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

下面是我成功使用了修复,但它会被多次调用。谨慎使用。

@objc func controllerDidChangeContent(controller: NSFetchedResultsController) { 
    tableView.endUpdates() // Only needed if you're calling tableView.beginUpdates() in controllerWillChangeContent. 

    if controller.fetchRequest.fetchLimit > 0 && controller.fetchRequest.fetchLimit < controller.fetchedObjects?.count { 
      controller.performFetch() 
      // Reload the table view section here 
     } 
    } 
} 
0

这是我的绝招:

我设置了NSFetchedResultsController的委托后,“拯救”上的NSManagedObjectContext实例方法被调用。

  1. 使用名称在你的UIViewController上设置一个观察者:eg。 “同步”
  2. 保存上下文后,发布该名称的通知:“同步”,并引发功能(在视图 - 控制),其设置委托

PS。记得要删除的观察者,如果你不需要它了