Reputation: 175
Does anyone know why executing a NSFetchRequest with fetchOffset set beyond the total number of matching records will cause a NSRangeException? Consider the following code:
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:@"MyRecord" inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entity];
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"checked == 1"];
[fetchRequest setPredicate:predicate];
fetchRequest.fetchOffset = 20;
fetchRequest.fetchLimit = 20;
NSError *error = nil;
NSArray *fetchedObjects = [self.managedObjectContext executeFetchRequest:fetchRequest error:&error];
If I only have 10 records in the core data storage but none of the matches the predicate filter, the last line will cause a NSRangeException:
*** Terminating app due to uncaught exception 'NSRangeException', reason: '*** -[NSMutableArray removeObjectsInRange:]: range {0, 20} extends beyond bounds for empty array'
*** First throw call stack:
(
0 CoreFoundation 0x04ee61e4 __exceptionPreprocess + 180
1 libobjc.A.dylib 0x04c658e5 objc_exception_throw + 44
2 CoreFoundation 0x04edae8d -[NSMutableArray removeObjectsInRange:] + 301
3 CoreData 0x017b4842 -[NSMappedObjectStore executeFetchRequest:withContext:] + 3762
4 CoreData 0x017b391e -[NSMappedObjectStore executeRequest:withContext:error:] + 222
5 CoreData 0x0173b7f2 -[NSPersistentStoreCoordinator executeRequest:withContext:error:] + 4466
6 CoreData 0x01738f56 -[NSManagedObjectContext executeFetchRequest:error:] + 566
....
)
Not sure if it matters, but the managed object context has a NSInMemoryStoreType storage type, and I did try saving the context before executing the query. What's interesting is that if my core data storage contains no "MyRecord" objects, the above code will work correctly and returns no result. But as soon as I have some objects (less than the fetchOffset count) the storage, I run into this issue.
Anyone know why does this happen?
Upvotes: 1
Views: 248
Reputation: 21244
If you look at the documentation for fetchLimit :
If you set a fetch limit, the framework makes a best effort, but does not guarantee, to improve efficiency. For every object store except the SQL store, a fetch request executed with a fetch limit in effect simply performs an unlimited fetch and throws away the unasked for rows.
The exception you're running into smells like a bug. As the documentation describes, for a store other than the NSSQLiteStoreType
, the rows outside fetchLimit are discarded - that is the removeObjectsInRange:
in your stack trace. The caller is not obligated to see how many objects a fetch would return before executing a fetch (and that number could change in between that check and the actual fetch), and the framework should "do the right thing" in these cases - before removing rows from the results, it should be performing a bounds check.
Please file a bug
Upvotes: 1
Reputation: 57050
Uhm, just like trying to access a range that is incorrect in an array, similar result will happen when trying to fetch objects with an incorrect offset.
Use -[NSManagedObjectContext countForFetchRequest:error:]
to find the number of registered objects in the context, and then perform some logic to ensure the range you provide is correct.
Upvotes: 0