NSFetchResultController
with NSFetchRequest
with fetchBatchSize = 20
always return all entities. What can it be? I didn't use sectionKeyPath and tried different sort descriptors, but it's still return all objects.
Thanks for replies!I will explain with details. I have an entity with two fields - distance and time. I have created NSFetchResultController:
func noticesFetcher() -> NSFetchedResultsController {
let fetchRequest = NSFetchRequest()
let defaultStore = RKManagedObjectStore.defaultStore()
let entity = NSEntityDescription.entityForName("Notice", inManagedObjectContext: defaultStore.mainQueueManagedObjectContext)
fetchRequest.entity = entity
let distanceSortDescriptor = NSSortDescriptor(key: "distance", ascending: true)
let timeSortDescriptor = NSSortDescriptor(key: "time", ascending: false)
let sortDescriptors = [distanceSortDescriptor, timeSortDescriptor]
fetchRequest.sortDescriptors = sortDescriptors
fetchRequest.fetchBatchSize = 20
let resultFetcher = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: defaultStore.mainQueueManagedObjectContext, sectionNameKeyPath: nil, cacheName: nil)
return resultFetcher
}
But when i perform fetcher i always have all my entities in database.(100)
func performFetcher(fetcher: NSFetchedResultsController?, filter: NSPredicate?, success: (() -> ())?, failure: ((NSError) -> (NSError))?) -> Bool {
NSFetchedResultsController.deleteCacheWithName(nil)
var resultPerform = false
fetcher?.fetchRequest.predicate = filter
do {
try fetcher?.performFetch()
resultPerform = true
if success != nil {
success!();
}
}
catch let error as NSError {
if failure != nil {
failure!(error)
}
}
return resultPerform
}
What can it be? The result that i want to get is pagination getter. I know that i can do it through limit and offset, but what a problem here? Thanks
Well, that depends on what you mean by "return all entities." I doubt it returns an array populated with all entities fully realized.
The batch size will fetch (in your case) only 20 entities at a time. Go ahead and look at the set of registered objects for the MOC and you can easily verify what is happening.
You can also fire up instruments and watch as the individual core data fetches take place.
No, batch size fetch not "only 20 entities", it fetches ALL entities. Can you make a test project and test this batch size?I'm sure you will have the same issue – Serd
@Serd, Answer providers are here to help you, and we are sometimes wrong. If you doubt their answer, then you should create a test case that demonstrates whether or not what they say is correct. Why in the world should they take extra time to do so, when you are the one with the problem, and they are the ones freely offering assistance?
If you doubt the answer, then you do the work, and if you find their answer in error, then provide either the correction or the evidence that the answer is incorrect.
However, you are new here, and in the hopes that you may learn from this experience, I'll provide you with a brief demonstration.
Setup the MOC for the test by adding NUM_OBJECTS
instances to the database.
NSUInteger const NUM_OBJECTS = 1000;
NSUInteger const INIT_BATCH_SIZE = 100;
- (void)setUp {
[super setUp];
helper = [[TestHelper alloc] init];
url = [[[NSURL fileURLWithPath:NSTemporaryDirectory()] URLByAppendingPathComponent:[[NSUUID UUID] UUIDString]] URLByAppendingPathComponent:@"foo.sqlite"];
[[NSFileManager defaultManager] createDirectoryAtURL:[url URLByDeletingLastPathComponent] withIntermediateDirectories:YES attributes:nil error:NULL];
@autoreleasepool {
NSEntityDescription *noticeEntity = [[NSEntityDescription alloc] init];
noticeEntity.name = @"Notice";
NSAttributeDescription *distance = [[NSAttributeDescription alloc] init];
distance.name = @"distance";
distance.attributeType = NSDoubleAttributeType;
NSAttributeDescription *time = [[NSAttributeDescription alloc] init];
time.name = @"time";
time.attributeType = NSDoubleAttributeType;
noticeEntity.properties = @[distance, time];
NSManagedObjectModel *model = [[NSManagedObjectModel alloc] init];
model.entities = @[noticeEntity];
NSPersistentStoreCoordinator *psc = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:model];
[psc addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:url options:nil error:NULL];
moc = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
moc.persistentStoreCoordinator = psc;
for (NSUInteger count = 0; count < NUM_OBJECTS; ) {
@autoreleasepool {
for (NSUInteger batchCount = 0; batchCount < INIT_BATCH_SIZE && count < NUM_OBJECTS; ++batchCount, ++count) {
NSManagedObject *notice = [NSEntityDescription insertNewObjectForEntityForName:@"Notice" inManagedObjectContext:moc];
double distance = ((double)arc4random_uniform(100000)) / (arc4random_uniform(100)+1);
double time = distance / (arc4random_uniform(100)+1);
[notice setValue:@(distance) forKey:@"distance"];
[notice setValue:@(time) forKey:@"time"];
}
[moc save:NULL];
[moc reset];
}
}
[moc save:NULL];
[moc reset];
}
}
Cleanup after the test...
- (void)tearDown {
[super tearDown];
[[NSFileManager defaultManager] removeItemAtURL:[url URLByDeletingLastPathComponent] error:NULL];
}
A couple of helper methods...
- (NSArray*)executeFetchWithBatchSize:(NSUInteger)batchSize {
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:@"Notice"];
fetchRequest.sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:@"distance" ascending:YES],
[NSSortDescriptor sortDescriptorWithKey:@"time" ascending:NO]];
fetchRequest.fetchBatchSize = batchSize;
return [moc executeFetchRequest:fetchRequest error:NULL];
}
- (NSUInteger)numberOfFaults {
NSUInteger numFaults = 0;
for (NSManagedObject *object in moc.registeredObjects) {
if (object.isFault) ++numFaults;
}
return numFaults;
}
A test for using the default batch size
- (void)testThatFetchRequestWitDefaultBatchSizeFetchesEverything {
XCTAssertEqual(0, moc.registeredObjects.count);
NSArray *fetched = [self executeFetchWithBatchSize:0];
XCTAssertEqual(NUM_OBJECTS, moc.registeredObjects.count);
XCTAssertEqual(NUM_OBJECTS, fetched.count);
XCTAssertEqual(NUM_OBJECTS, [self numberOfFaults]);
[[fetched objectAtIndex:1] valueForKey:@"distance"];
XCTAssertEqual(NUM_OBJECTS, moc.registeredObjects.count);
XCTAssertEqual(NUM_OBJECTS, fetched.count);
XCTAssertEqual(NUM_OBJECTS-1, [self numberOfFaults]);
}
A test for using a non-default batch size
- (void)testThatFetchRequestWithExplicitBatchSizeOnlyFetchesTheNumberRequested {
XCTAssertEqual(0, moc.registeredObjects.count);
NSUInteger const BATCH_SIZE = 20;
NSArray *fetched = [self executeFetchWithBatchSize:BATCH_SIZE];
XCTAssertEqual(0, moc.registeredObjects.count);
XCTAssertEqual(NUM_OBJECTS, fetched.count);
XCTAssertEqual(0, [self numberOfFaults]);
[[fetched objectAtIndex:1] valueForKey:@"distance"];
XCTAssertEqual(BATCH_SIZE, moc.registeredObjects.count);
XCTAssertEqual(NUM_OBJECTS, fetched.count);
XCTAssertEqual(BATCH_SIZE-1, [self numberOfFaults]);
}
Thanks for your code! But i have a problem with NSFetchResultController and his NSFetchRequest, that have batch size(It's fetches all entities – Serd
Habits die hard. Instead of making that assertion, you should have taken the code I gave you and modified it slightly to test with a fetched results controller, and you would then confirm for yourself that your assertion "It's fetches all entities" is not true. For example...
Modify the helpers just a tad...
- (NSFetchRequest*)fetchRequestWithBatchSize:(NSUInteger)batchSize {
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:@"Notice"];
fetchRequest.sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:@"distance" ascending:YES],
[NSSortDescriptor sortDescriptorWithKey:@"time" ascending:NO]];
fetchRequest.fetchBatchSize = batchSize;
return fetchRequest;
}
- (NSArray*)executeFetchWithBatchSize:(NSUInteger)batchSize {
return [moc executeFetchRequest:[self fetchRequestWithBatchSize:batchSize] error:NULL];
}
- (NSFetchedResultsController*)executeFetchUsingFetchedResultsControllerWithBatchSize:(NSUInteger)batchSize {
NSFetchedResultsController *frc = [[NSFetchedResultsController alloc] initWithFetchRequest:[self fetchRequestWithBatchSize:batchSize] managedObjectContext:moc sectionNameKeyPath:nil cacheName:nil];
[frc performFetch:NULL];
return frc;
}
And change the existing tests slightly to add tests for FRC.
- (void)testThatFetchRequestWitDefaultBatchSizeFetchesEverythingEvenWithFetchedResultsController {
XCTAssertEqual(0, moc.registeredObjects.count);
NSFetchedResultsController *frc = [self executeFetchUsingFetchedResultsControllerWithBatchSize:0];
XCTAssertEqual(NUM_OBJECTS, moc.registeredObjects.count);
XCTAssertEqual(NUM_OBJECTS, frc.fetchedObjects.count);
XCTAssertEqual(NUM_OBJECTS, [self numberOfFaults]);
[[frc.fetchedObjects objectAtIndex:1] valueForKey:@"distance"];
XCTAssertEqual(NUM_OBJECTS, moc.registeredObjects.count);
XCTAssertEqual(NUM_OBJECTS, frc.fetchedObjects.count);
XCTAssertEqual(NUM_OBJECTS-1, [self numberOfFaults]);
}
- (void)testThatFetchRequestWithExplicitBatchSizeOnlyFetchesTheNumberRequestedEvenWithFetchedResultsController {
XCTAssertEqual(0, moc.registeredObjects.count);
NSUInteger const BATCH_SIZE = 20;
NSFetchedResultsController *frc = [self executeFetchUsingFetchedResultsControllerWithBatchSize:20];
XCTAssertEqual(moc, frc.managedObjectContext);
XCTAssertEqual(0, moc.registeredObjects.count);
XCTAssertEqual(NUM_OBJECTS, frc.fetchedObjects.count);
XCTAssertEqual(0, [self numberOfFaults]);
[[frc.fetchedObjects objectAtIndex:1] valueForKey:@"distance"];
XCTAssertEqual(BATCH_SIZE, moc.registeredObjects.count);
XCTAssertEqual(NUM_OBJECTS, frc.fetchedObjects.count);
XCTAssertEqual(BATCH_SIZE-1, [self numberOfFaults]);
}