iOS CoreData批量/批量插入(sqlite TRANSACTION)

时间:2015-10-08 11:06:56

标签: ios core-data

我在.txt文件中有16000条记录,并希望使用核心数据在数据库中插入所有数据。但是操作性能不佳需要花费很多时间来读取数据从txt文件到数据库(使用coredata)。

在android中,我使用 SQLite TRANSACTION来获得最佳性能并且工作正常。

但在iOS无法找到方法。

注意:我已尝试过以下帖子,但所有提供的参考网址都显示错误404

iOS CoreData batch insert?

3 个答案:

答案 0 :(得分:2)

您希望使用完全独立的堆栈进行保存(在专用队列上创建新的持久性存储协调器和托管对象上下文)。这是为了在iOS7 +中默认使用WAL - on。这允许您在不阻止正在阅读的上下文的情况下编写。

您还希望批量导入,每个批次都在自己的自动释放池中。保存并重置MOC。

我忽略了这个例子中的错误,为了清楚一个与处理错误无关的例子......不要这样做......

- (void)importFromURL:(NSURL*)url batchSize:(NSUInteger)batchSize {
    // Open the URL so it can be used to read the text file

    // Create a new PSC, attached to the same store(s) as the PSC used by main context
    NSManagedObjectModel *model = // same MOM used by your main context
    NSPersistentStoreCoordinator *psc = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:model];
    [psc addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:NULL];

    // A private-queue MOC
    NSManagedObjectContext *moc = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
    moc.persistentStoreCoordinator = psc;
    moc.undoManager = nil;

    // Load all objects from the file, in batches
    NSUInteger total = 0;
    Record *record = nil;
    do {
        NSUInteger count = 0;
        @autoreleasepool {
            while (count < batchSize && (record = [self fetchNextRecord]) != nil) {
                [self createManagedObjectFromRecord:record inManagedObjectContext:moc];
                ++count;
            }
            [moc save:NULL]; // don't ignore the error
            [moc reset];
        }
        total += count;
    } while (record != nil);

    // Post a notification indicating that the import has finished.
    // This allows your main context to refetch from the store.
    [[NSNotificationCenter defaultCenter] postNotificationName:@"DidImport" object:self userInfo:@{ @"url":url, @"total":@(total)}];

请注意,这假设您没有任何关系,而您只是加载直接对象。

如果你有关系,你应该先加载对象,然后再建立关系。

如果您的数据集更像是一堆相关对象的小集群,那么您应该批量加载每个小集群,加载对象,然后连接关系,然后保存并移动到下一批。

那么,该示例代码的作用是什么?

它创建了一个完全独立的核心数据堆栈,在导入过程中可以实现更好的并发性。

它以小批量加载对象。这可以防止内存分配失控,并使更新大小保持可管理性。如果您让其他上下文观察更改并自动将它们合并,这将有所帮助。小批量对执行合并的其他线程的影响较小。

在每个批次将数据提交到商店后保存。重置上下文释放与上下文关联的任何内存,并允许下一批重新开始。这有助于限制内存增长。

自动释放池确保在每个批处理结束时释放所有自动释放的对象,这也有助于减少内存占用。

最后的通知允许您通知其他代码从实际核心数据数据库文件中重新提取。

对于非常大的导入,这可能比观察上下文的保存和每次合并更有效。

这显然不是正常工作的代码,但它会向您展示如何继续。如果您遵循此模板,您应该能够快速有效地导入。

答案 1 :(得分:0)

由于数据是静态的,您只需要通过Core Data在sqlite文件中预加载所有数据。您可以阅读here

答案 2 :(得分:0)

这可以通过以下提示轻松有效地完成:

  • 导入时,分批保存(例如,每500条记录,试验以获得最佳效果)
  • 使用@autoreleasepool来优化内存
  • 关闭undoManager(将其设为nil
  • 使用子上下文在后台执行插入操作。有关Core Data备份操作的最佳设置,请参阅下面的方案。

    rootContext(background) - 保存到持久性存储 mainContext(主线程) - 用于UI workerContext(背景) - 执行繁重的工作(例如许多插入)

当你保存工作者上下文时,UI会有效地更新(它全部在内存中),同时在后台完成数据创建和保存到物理数据库的繁重工作。

从CSV文件中插入了200.000多条记录,表现非常出色。

请注意,在模拟器中完成所有这些操作(即使没有优化),然后将完成的数据库sqlite文件放入其他地方建议的捆绑包中也是可行且良好的做法。