7

好的。这是与此处完全相同的问题:Why is NSFetchedResultsController loading all rows when setting a fetch batch size?

但是他的解决方案并不能解决我的问题。

我有一个有几千条记录的屏幕,加载它们的速度很慢。我将批处理大小设置为 30(大约是屏幕上单元格的三倍),但由于某种原因,它仍然循环并加载所有批处理。

这是代码

- (NSFetchedResultsController *)guestCardFetchedResultsController
{
    if (guestCardFetchedResultsController != nil) {
        return guestCardFetchedResultsController;
    }

    // SELECT * from GuestCard
    NSFetchRequest* fetchRequest = [[NSFetchRequest alloc] init];
    NSEntityDescription *entity = [NSEntityDescription entityForName:@"GuestCard" inManagedObjectContext:self.context];
    [fetchRequest setEntity:entity];
    // ORDER BY updated DESC
    NSSortDescriptor* updatedSortDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"created" ascending:NO];
    [fetchRequest setSortDescriptors:@[updatedSortDescriptor]];
    fetchRequest.fetchBatchSize = 30;
    NSString *cacheName = self.isReportProblemView ? @"reportProblemGuestCardsAll" : @"guestCardsAll";

    [NSFetchedResultsController deleteCacheWithName:cacheName];
    NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.context sectionNameKeyPath:@"sectionIdentifier" cacheName:cacheName];
    aFetchedResultsController.delegate = self;
    self.guestCardFetchedResultsController = aFetchedResultsController;

    // Clean up

    NSError *error = nil;
    if (![[self guestCardFetchedResultsController] performFetch:&error]) {
    }

    return self.guestCardFetchedResultsController;
}

在这种情况下,我没有做任何非常有趣的事情。这是一些委托代码(不包括单元格创建,我确认仅调用屏幕上的单元格数量):

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
    if ([self.guestCardFetchedResultsController.fetchedObjects count] == 0) {
        return 1;
    }
    // Return the number of sections.
    return [[self.guestCardFetchedResultsController sections] count];
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    if ([self.guestCardFetchedResultsController.fetchedObjects count] == 0) {
        return 1;
    }
    // Return the number of rows in the section.
    id <NSFetchedResultsSectionInfo> sectionInfo = [guestCardFetchedResultsController sections][section];
    return [sectionInfo numberOfObjects];
}

- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
    if ([self.guestCardFetchedResultsController.fetchedObjects count] == 0) {
        return @"";
    }

    return [[self.guestCardFetchedResultsController sections][section] name];
}
4

3 回答 3

8

在阅读了NSFetchedResultsController 初始化程序上的文档后,我认为问题在于您在获取请求(已创建)中有一个排序,它与部分键路径(sectionIdentifier)自然排序不同。我正在查看的文档中的特定句子说:

如果此键路径与 fetchRequest 中第一个排序描述符指定的不同,则它们必须生成相同的相对排序

我建议修改您的获取请求以先对 sectionIdentifier 进行排序,然后再创建。我认为这会解决你的问题。

 NSSortDescriptor* updatedSortDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"created" ascending:NO];
 NSSortDescriptor* sectionSortDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"sectionIdentifier" ascending:NO];
 // It's critical the sectionSortDescriptor is first
 [fetchRequest setSortDescriptors:@[sectionSortDescriptor, updatedSortDescriptor]];

请注意,如果createdorsectionIdentifier属性实际上是实体类上的方法,那肯定会强制 Core Data 在排序/分段之前加载所有数据,因为它需要首先在每个实体上执行该方法。

于 2013-10-19T17:28:09.117 回答
5

当使用fetchBatchSize而不是返回 NSArray 中的对象时,它会返回一个特殊的批处理错误数组。尽管它确实加载了所有对象 ID,但数组中的所有对象都是错误的,这意味着它们的数据没有被加载,即像占位符对象一样。然后,当您在访问对象的属性时循环数组(或表重新加载)时,它会对该对象的属性执行数据库查询,包括直到 fetchBatchSize 的下一个对象(顺便说一下,最小值似乎是 4)。通过查看查询来调试此内部行为的最佳方法是编辑您的方案并添加一个运行参数:

-com.apple.CoreData.SQLDebug 1

这将输出以控制台各种查询。您将看到对行 ID 的第一个查询,然后是每批数据的加载。

因此,您可能遇到的是加载所有行 ID 和创建故障对象的速度很慢,但这应该很快。

另一种可能性是,当它访问 sectionIdentifier 属性来构建这些部分时,这也会导致对象也被加载,从而导致所有批次都被加载。为了解决这个问题,您可以尝试将 propertiesToFetch 设置为包含 sectionIdentifier,这样第一个查询应该加载到该属性中,然后在访问它时不应该加载该对象。

于 2015-12-11T14:36:59.747 回答
-1

我有同样的问题,最后我得出结论,它应该可以正常工作。

当您设置 fetchBatchSize 时,fetchRequest 会将所有数据加载到缓存中,否则此参数将无用。我猜它是用来在加载后使用数据的。但是当你得到对象时,[fetchResultController objectAtIndexPath:indexPath]它是由 ManagedObjectContext (我想)实现的,而不是由 fetchRequest 实现的。

因此,您可以将 fetchBatchSize 设置为 0,并且每次访问故障时都会从持久存储中请求它。或者您可以设置其他值,并在每次滚动表视图时设置 fetchLimit 和 fetchOffset。但是您仍然必须知道总体对象数量。

于 2013-10-18T21:26:18.143 回答