5

我正在使用 NSFetchResultsController 在 UITableView 中显示 100,000 多条记录。这可行,但速度很慢,尤其是在 iPad 1 上。加载可能需要 7 秒,这对我的用户来说是一种折磨。

我也希望能够使用部分,但这至少会增加 3 秒的延迟时间。

这是我的 NSFetchResultsController:

- (NSFetchedResultsController *)fetchedResultsController {

    if (self.clientsController != nil) {
        return self.clientsController;
    }

    NSFetchRequest *request = [[NSFetchRequest alloc] init];
    NSEntityDescription *entity = [NSEntityDescription entityForName:@"Client" inManagedObjectContext:self.managedObjectContext];
    [request setEntity:entity];
    [request setPredicate:[NSPredicate predicateWithFormat:@"ManufacturerID==%@", self.manufacturerID]];
    [request setFetchBatchSize:25];

    NSSortDescriptor *sort = [[NSSortDescriptor alloc]  initWithKey:@"UDF1" ascending:YES];
    NSSortDescriptor  *sort2= [[NSSortDescriptor alloc] initWithKey:@"Name" ascending:YES];
    [request setSortDescriptors:[NSArray arrayWithObjects:sort, sort2,nil]];

    NSArray *propertiesToFetch = [[NSArray alloc] initWithObjects:@"Name", @"ManufacturerID",@"CustomerNumber",@"City", @"StateProvince",@"PostalCode",@"UDF1",@"UDF2", nil];
    [request setPropertiesToFetch:propertiesToFetch];

    self.clientsController =
    [[NSFetchedResultsController alloc] initWithFetchRequest:request
                                        managedObjectContext:self.managedObjectContext sectionNameKeyPath:nil
                                                   cacheName:nil];

    return self.clientsController;

}

我有一个在我的 NSPredicate 中使用的 ManufacturerID 索引。这似乎是一个非常基本的 NSFetchRequest - 我能做些什么来加快速度?还是我刚刚达到了限制?我肯定错过了什么。

4

2 回答 2

7

第一:您可以NSFetchedResultsController在第一次获取后使用 的缓存来加快显示速度。这应该很快下降到几分之一秒。

第二:您可以尝试仅显示第一个屏幕,然后在后台获取其余部分。我通过以下方式执行此操作:

  • 当视图出现时,检查您是否有第一页缓存。
  • 如果没有,我会获取第一页。您可以通过设置获取请求的fetchLimit.
    • 如果您正在使用部分,请执行两次快速提取以确定第一个部分标题和记录。
  • 在后台线程中使用您的 long fetch 填充第二个 fetched results 控制器。
    • 您可以创建一个子上下文并使用performBlock:
    • 使用dispatch_async().
  • 将第二个 FRC 分配给表视图并调用reloadData.

这在我最近的一个记录超过 200K 的项目中效果很好。

于 2013-02-05T15:00:42.307 回答
0

我知道@Mundi 提供的答案已被接受,但我已经尝试实施它并遇到了问题。具体来说,第二个 FRC 创建的对象将基于另一个线程的 ManagedObjectContext。由于这些对象不是线程安全的,并且在另一个线程上属于它们自己的 MOC,因此我找到的解决方案是在加载对象时对其进行故障排除。所以在 cellForRowAtIndexPath 我添加了这一行:

NSManagedObject *object = [self.fetchedResultsController objectAtIndexPath:indexPath];
object = (TapCellar *)[self.managedObjectContext existingObjectWithID:[object objectID] error:nil];

然后你就有了一个对应于你所在线程的对象。另外需要注意的是,你对对象所做的更改不会反映在后台 MOC 中,因此你必须协调它们。我所做的是将后台 MOC 设为私有队列 MOC,而前台是它的子 MOC,如下所示:

NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
if (coordinator != nil) {
  _privateManagedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
  [_privateManagedObjectContext setPersistentStoreCoordinator:coordinator];

  _managedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
  [_managedObjectContext setParentContext:_privateManagedObjectContext];

}

现在,当我在主线程中进行更改时,我可以通过执行以下操作轻松协调它们:

if ([self.managedObjectContext hasChanges]) {
   [self.managedObjectContext performBlockAndWait:^{
      NSError *error = nil;
      ZAssert([self.managedObjectContext save:&error], @"Error saving MOC: %@\n%@",
             [error localizedDescription], [error userInfo]);
   }];
}

我等待它的返回,因为此时我要重新加载表数据,但如果你愿意,你可以选择不等待。即使对于 30K+ 记录,这个过程也非常快,因为通常只更改一两个。

希望这可以帮助那些坚持这一点的人!

于 2014-06-17T14:01:48.510 回答