Objective c 核心数据请求的分页结果
我有一个相对简单的核心数据sqlite数据库。我试图从数据库中一页一页地获取结果Objective c 核心数据请求的分页结果,objective-c,ios,core-data,Objective C,Ios,Core Data,我有一个相对简单的核心数据sqlite数据库。我试图从数据库中一页一页地获取结果 NSFetchRequest* request = [[[NSFetchRequest alloc] init] autorelease]; [request setEntity:[...]]; [request setPredicate:[NSPredicate predicateWithFormat:@"flaggedTime != nil"]]; NSSortDescri
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(数据库中大约有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) 总数:34 获取偏移量:0,限制:30 第1页计数:30 取回偏移量:30,限制:30 第2页计数:30(错误:应给出4)
我刚刚创建了一个演示项目,试图以最简单的形式重新创建您的场景。我创建了一个空白项目,添加了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];
使用您的代码,我可以让它正常运行。这是在模拟器上运行iOS5.0时完成的。在我看来,您的代码是正确的,因为您正在尝试完成,所以在针对未保存的上下文运行提取请求时,
NSFetchRequest
的pageOffset
属性(有时?)会被忽略。请注意,在code by OP上下文中是如何从不保存的,而在@kcharwood所提供答案的代码片段中,它实际上是保存的。以下是一个例子:
- (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];
核心数据内置分页功能,非常简单,只需在获取请求上设置fetchBatchSize。当将所有对象的fetch just查询设置为faults时,基本上只是一个指针及其行索引,它是一种称为批处理faulting数组的特殊数组的最小内存。我认为这在大多数情况下都很好,尽管我还没有研究过它对大量行使用多少内存。然后,当您在访问故障记录的属性时循环此数组时,它会在数据库中查询该记录和下一个记录,直到您为批量大小设置的数量为止。这允许您像正常情况一样逐个循环结果,但在后台它会批量加载数据。从iOS 9.2开始,最小批量大小为4,因此没有任何一点设置低于该值的数字 我们可以看看设置页面大小和页面索引的代码吗?此外,显示结果的代码?与此无关。问题就在这里。我知道你不相信我,你希望在其他地方找到一个简单的解决方案,但是方法中的参数就是我上面写的。但我很好奇-你真的有一个同时使用fetchLimit和fetchOffset的工作代码吗?我有一个使用fetchLimit、fetchOffset和SortDescriptor的工作代码-就像你一样。我的代码运行良好。打开数据库或请求设置时会有差异吗?我知道文档中的状态是“在嵌套请求(如子查询)中忽略偏移量”。但我认为我的请求非常简单。@Sulthan您可能必须共享momd文件以帮助重现错误。我认为OP的问题是概念上与“页面”和“分页”混淆。在这个隐含的数据模型或显式代码中,没有任何内在的页面。他没有像编程中通常使用的那样“翻页”任何东西,仍然试图在新创建的模型上重现这个问题。请耐心一点。这个问题早该提了,我相信这就是当时的实际问题。未保存的上下文。由于这个答案,我可以在我的程序中找到一个错误。我只想补充一点,获取的对象是未保存的对象。我不敢相信苹果没有记录这个问题。需要注意的是,这个例子之所以有效,是因为你使用的上下文是基础上下文,而不是它的子上下文。在子上下文中运行相同种类的场景仍然会产生不好的结果,即使保存到将更改保存到持久存储中。这是因为fetchOffset属性依赖sqlite和磁盘上的db来运行。这确实会在objective-c中引起警告,但它们只会返回坏结果。
- (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];
2014-03-01 11:30:06.986 coredatatest[19771:70b] fetched objects: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
count: 30
2014-03-01 11:30:06.990 coredatatest[19771:70b] fetched objects: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
count: 30
2014-03-01 11:30:06.995 coredatatest[19771:70b] fetched objects: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
count: 30
2014-03-01 11:30:06.997 coredatatest[19771:70b] fetched objects: 30 31 32 33
count: 4