我有一个相对简单的核心数据sqlite数据库。我试图一次从DB获取结果。
NSFetchRequest* request = [[[NSFetchRequest alloc] init] autorelease];
[request setEntity:[...]];
[request setPredicate:[NSPredicate predicateWithFormat:@"flaggedTime != nil"]];
NSSortDescriptor* sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"flaggedTime" ascending:NO];
[request setSortDescriptors:[NSArray arrayWithObject:sortDescriptor]];
[request setFetchLimit:pageSize];
[request setFetchOffset:((pageIndex - 1) * pageSize)];
NSArray* results = [self.context executeFetchRequest:request error:NULL];
pageSize为30,测试数据的pageIndex为1,2,3或4(DB中有大约80个项目,因此pageIndex = 4应该不返回任何项目)。 谓词和排序工作正常,结果成功返回。获取限制也可以正常工作。没有错误返回。
问题:我总是从第一页获得结果,就像没有设置fetchOffset一样。我试图删除谓词和排序但无济于事。 我可以使fetchOffset工作的唯一情况是当我使用30以下的值时。当然,这对于分页来说毫无意义......
有人知道为什么吗?我会非常感谢每一个答案。
更新:我说的是iOS。测试4.2和5.0。
更新2:简化问题。
NSFetchRequest* request = [[[NSFetchRequest alloc] init] autorelease];
[request setEntity:[...];
NSError* error = nil;
NSManagedObjectContext* context = [...];
NSUInteger count = [context countForFetchRequest:request error:&error];
assert(error == nil);
NSLog(@"Total count: %u", count);
request.fetchOffset = 0;
request.fetchLimit = 30;
NSLog(@"Fetch offset: %u, limit: %u", request.fetchOffset, request.fetchLimit);
NSArray* page1 = [context executeFetchRequest:request error:&error];
assert(error == nil);
NSLog(@"Page 1 count: %u", page1.count);
request.fetchOffset = 30;
request.fetchLimit = 30;
NSLog(@"Fetch offset: %u, limit: %u", request.fetchOffset, request.fetchLimit);
NSArray* page2 = [context executeFetchRequest:request error:&error];
assert(error == nil);
NSLog(@"Page 2 count: %u", page2.count);
得到:
Total count: 34 Fetch offset: 0, limit: 30 Page 1 count: 30 Fetch offset: 30, limit: 30 Page 2 count: 30 (ERROR: should give 4)
答案 0 :(得分:19)
我刚刚创建了一个演示项目,尝试以最简单的形式重新创建场景。我创建了一个空白项目,添加了34个对象,然后使用上面列出的完全相同的代码查询它。以下是示例:
CDAppDelegate * delegate = (CDAppDelegate*)[[UIApplication sharedApplication] delegate];
NSManagedObjectContext * context = [delegate managedObjectContext];
for(int i = 0; i < 34;i++){
CDObject * object = [NSEntityDescription insertNewObjectForEntityForName:@"CDObject"
inManagedObjectContext:context];
[object setValue:i];
}
[delegate saveContext];
NSFetchRequest* request = [[[NSFetchRequest alloc] init] autorelease];
[request setEntity:[NSEntityDescription entityForName:@"CDObject"
inManagedObjectContext:context]];
NSError* error = nil;
NSUInteger count = [context countForFetchRequest:request error:&error];
assert(error == nil);
NSLog(@"Total count: %u", count);
request.fetchOffset = 0;
request.fetchLimit = 30;
NSLog(@"Fetch offset: %u, limit: %u", request.fetchOffset, request.fetchLimit);
NSArray* page1 = [context executeFetchRequest:request error:&error];
assert(error == nil);
NSLog(@"Page 1 count: %u", page1.count);
request.fetchOffset = 30;
request.fetchLimit = 30;
NSLog(@"Fetch offset: %u, limit: %u", request.fetchOffset, request.fetchLimit);
NSArray* page2 = [context executeFetchRequest:request error:&error];
assert(error == nil);
NSLog(@"Page 2 count: %u", page2.count);
[request release];
日志如下所示:
2011-11-04 14:53:04.530 CDCoreDataTest[77964:207] Total count: 34
2011-11-04 14:53:04.531 CDCoreDataTest[77964:207] Fetch offset: 0, limit: 30
2011-11-04 14:53:04.532 CDCoreDataTest[77964:207] Page 1 count: 30
2011-11-04 14:53:04.533 CDCoreDataTest[77964:207] Fetch offset: 30, limit: 30
2011-11-04 14:53:04.533 CDCoreDataTest[77964:207] Page 2 count: 4
使用您的代码,我能够让它运行起来没有问题。这是通过在模拟器上运行的iOS 5.0完成的。您的代码看起来正确,因为您正在尝试完成,因此必须对获取请求或上下文本身进行一些操作...
答案 1 :(得分:13)
当您针对未保存的上下文运行获取请求时,pageOffset
的{{1}}属性(有时?)会被忽略。请注意,OP上下文中的代码从未保存过,而在@kcharwood附加到答案的代码片段中,它实际上已保存。这是一个例子:
NSFetchRequest
日志:
- (void) printArrayOfTestEntities:(NSArray *)array{
NSMutableString * s = [NSMutableString new];
NSArray * numbers = [array valueForKey:@"someField"];
for (NSNumber * number in numbers){
[s appendString:[NSString stringWithFormat:@"%d ", [number intValue]]];
}
NSLog(@"fetched objects: %@ \rcount: %d", s, array.count);
}
/* example itself */
NSManagedObjectContext *context = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
[context setPersistentStoreCoordinator:persistentStoreCoordinator];
for(int i = 0; i < 34;i++){
NSManagedObject * object = [NSEntityDescription insertNewObjectForEntityForName:@"TestEntity"
inManagedObjectContext:context];
[object setValue:@(i) forKey:@"someField"];
}
NSFetchRequest * request = [NSFetchRequest fetchRequestWithEntityName:@"TestEntity"];
request.sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:@"someField" ascending:YES]];
request.fetchLimit = 30;
NSArray * result = [context executeFetchRequest:request error:nil];
[self printArrayOfTestEntities:result];
request.fetchOffset = 30;
result = [context executeFetchRequest:request error:nil];
[self printArrayOfTestEntities:result];
[context save:&error];
request.fetchOffset = 0;
result = [context executeFetchRequest:request error:nil];
[self printArrayOfTestEntities:result];
request.fetchOffset = 30;
result = [context executeFetchRequest:request error:nil];
[self printArrayOfTestEntities:result];
答案 2 :(得分:0)
核心数据内置了分页,它非常简单,只需在获取请求中设置fetchBatchSize即可。当设置fetch时,只查询所有对象作为错误,基本上只是一个指针及其行索引,这是一个特殊类型的数组,称为批处理错误数组。我想象在大多数情况下这都很好,尽管我还没有仔细研究它将用于大量行的内存量。然后,当您访问故障记录的属性时循环此数组时,它会在数据库中查询该记录,然后查询下一个记录,直到您为批量大小设置的数字。这允许您像往常一样逐个循环结果,但在后台它会批量加载数据。从iOS 9.2开始,最小批量大小为4,因此没有点设置低于该值的数字。