核心数据NSFetchResultController批量大小

时间:2015-10-22 14:11:54

标签: ios core-data nsfetchedresultscontroller nsfetchrequest

NSFetchResultControllerNSFetchRequest fetchBatchSize = 20的{​​{1}}始终返回所有实体。它能是什么?我没有使用sectionKeyPath并尝试了不同的排序描述符,但它仍然返回所有对象。

感谢您的回复!我会详细解释。 我有一个有两个领域的实体 - 距离和时间。 我创建了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
}

但是当我执行fetcher时,我总是将所有实体都放在数据库中。(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
}

它能是什么? 我想得到的结果是分页吸气剂。我知道我可以通过限制和抵消来做到这一点,但这里有什么问题? 感谢

2 个答案:

答案 0 :(得分:4)

那么,这取决于你所说的“归还所有实体”。我怀疑它会返回一个填充了所有实体完全实现的数组。

批量大小一次只能获取(在您的情况下)20个实体。继续查看MOC的注册对象集,您可以轻松验证发生了什么。

您还可以启动仪器并观察各个核心数据的提取情况。

  

不,批量大小获取不“仅20个实体”,它获取所有实体。   你能做一个测试项目并测试这个批量吗?我相信你会的   有同样的问题 - 塞尔德

@Serd,答案提供者随时为您提供帮助,我们有时也错了。如果你怀疑他们的答案,那么应该创建一个测试用例来证明他们所说的是否正确。为什么在世界上他们应该花更多的时间这样做,当你是那个有问题的人时,他们是那些自由提供帮助的人?

如果你怀疑答案,那么做这项工作,如果你错误地找到答案,那么提供更正或答案不正确的证据。

但是,你是新来的,希望你可以从这次经历中学习,我会为你提供一个简短的演示。

通过向数据库添加NUM_OBJECTS实例来设置测试的MOC。

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];
    }
}

测试后的清理......

- (void)tearDown {
    [super tearDown];
    [[NSFileManager defaultManager] removeItemAtURL:[url URLByDeletingLastPathComponent] error:NULL];
}

一些辅助方法......

- (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;
}

使用默认批量大小的测试

- (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]);
}

使用非默认批量大小的测试

- (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]);
}
  

感谢您的代码!但我有一个问题   NSFetchResultController和他的NSFetchRequest,有批处理   大小(它取出所有实体 - 塞尔德

习惯很难受。你应该使用我给你的代码并稍微修改它以使用获取的结果控制器进行测试,然后你自己确认你的断言“它取出所有实体”是不正确的,而不是做出那个断言。例如......

稍微修改一下帮助......

- (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;
}

稍微更改现有测试以添加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]);
}

答案 1 :(得分:1)

fetchBatchSize只是Core Data使用的一项效率指标,用于确保尽可能少记录。坦率地说,99%的情况下完全没有必要,因此您通常可以安全地删除那一行多余的代码。

如果要将获取限制为一定数量的记录(类似于SQL LIMIT参数,通常与排序结合使用),则必须设置fetch的fetchLimit属性请求。

fetchRequest.fetchLimit = 20