【问题标题】:Core Data NSFetchResultController batch size核心数据 NSFetchedResultsController 批量大小
【发布时间】:2015-10-22 14:11:54
【问题描述】:

NSFetchResultControllerNSFetchRequestfetchBatchSize = 20 总是返回所有实体。会是什么?我没有使用 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
}

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

【问题讨论】:

    标签: ios core-data nsfetchedresultscontroller nsfetchrequest


    【解决方案1】:

    嗯,这取决于您所说的“返回所有实体”是什么意思。我怀疑它会返回一个填充了所有已完全实现的实体的数组。

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

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

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

    @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,有批处理 size(它获取所有实体 - Serd

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

    稍微修改一下助手...

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

    【讨论】:

    • 不,批量获取不是“仅 20 个实体”,而是获取所有实体。你能做一个测试项目并测试这个批量大小吗?我相信你会遇到同样的问题
    • 感谢您的代码!但我对 NSFetchResultController 和他的 NSFetchRequest 有问题,它们有批量大小(它获取所有实体
    • 有点晚了,但我希望它可以帮助那里的人。无法可靠地回答问题;因为如果在父/子关系中使用“NSManagedObjectContext”,则“fetchBatchSize”将被忽略。您需要直接在NSPersistentStoreCoordinator 中创建一个NSMainQueueConcurrencyType 类型的附加NSManagedObjectContext。有关更多信息,请参阅stackoverflow.com/a/11470560/4457396,并搜索 fetchBatchSize 和父子上下文。
    • 实际上,您可能会得到列表,而且看起来比批量大小要大。但是达到批量大小的对象都在内存中,其余的都是错误的。我们可以说,在内存游标中的数据库术语。
    【解决方案2】:

    fetchBatchSize 只是 Core Data 用来确保一次获得尽可能少的记录的一种效率措施。坦率地说,这在 99% 的情况下是完全没有必要的,因此您通常可以安全地删除那行多余的代码。

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

    fetchRequest.fetchLimit = 20
    

    【讨论】:

    • 我明白,我可以用限制/偏移属性替换 fetchBatchSize,但这不是一个好的解决方案。你能测试一下 fetchBatchSize 属性吗?
    • 测试什么?这一切都在我的回答中进行了解释。
    • 他正在尝试获取 20 条记录,然后再加载 20 条记录,然后继续这样做,直到达到 100 条的目标。您的回答不好。
    • 我的回答总结了文档:developer.apple.com/documentation/coredata/nsfetchrequest/…。如果您认为这不好,请告诉 Apple。
    猜你喜欢
    • 1970-01-01
    • 2015-10-20
    • 1970-01-01
    • 2011-01-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多