【问题标题】:Inserting child records is slow in coredata在 coredata 中插入子记录很慢
【发布时间】:2018-07-18 11:02:35
【问题描述】:

我在名为 Verse 的关系中存储了近 7K 项。我有另一个名为 Translation 的关系,它需要通过来自 JSON 文件的单个调用来加载 7K 相关项目。

这是我的代码:

let container = getContainer()
    container.performBackgroundTask() { (context) in
        autoreleasepool {


        for row in translations{
            let t = Translation(context: context)
            t.text = (row["text"]! as? String)!
            t.lang = (row["lang"]! as? String)!
            t.contentType = "Verse"
            t.verse = VerseDao.findById(row["verse_id"] as! Int16, context: context) 
// this needs to make a call to the database to retrieve the approparite Verse instance. 
        }

        }
        do {
           try context.save()
        } catch {
            fatalError("Failure to save context: \(error)")
        }
        context.reset()
    }

findById 方法的代码。

static func findById(_ id: Int16, context: NSManagedObjectContext) -> Verse{

    let fetchRequest: NSFetchRequest<Verse>
    fetchRequest = Verse.fetchRequest()
    fetchRequest.predicate = NSPredicate(format: "verseId == %@", id)
    fetchRequest.includesPropertyValues = false
    fetchRequest.fetchLimit = 1

    do {
        let results =
            try context.fetch(fetchRequest)
        return results[0]
    } catch let error as NSError {
        print("Could not fetch \(error), \(error.userInfo)")
        return Verse()

    }
}

在我添加VerseDao.findById 之前,这一切正常,这使得整个过程非常缓慢,因为它必须为每个对象向Coredata 数据库发出请求。

我尽我所能限制获取属性的数量并使用 NSFetchedResultsController 进行数据获取,但没有运气。

我想知道是否有任何方法可以更有效地插入子记录?谢谢。

【问题讨论】:

  • 请向我们展示 VerseDao.findById 的代码。
  • @user3727099 已更新。谢谢。
  • 不确定它是否有很大的不同,但你真的希望在给定verseId 的 findById 中找到多个对象,否则你可以跳过排序描述符
  • @JoakimDanielson 好点。我删除了它,但操作似乎仍然很慢。
  • 按照答案之一的建议更新了 findById 的代码。

标签: swift performance core-data relationship


【解决方案1】:

假设你的存储类型是持久存储类型是 sqlite (NSSQLiteStoreType):

你应该检查的第一件事是你是否在 Verse 对象的 verseId 属性上有一个核心数据获取索引。有关获取索引的一些介绍性链接,请参阅 this stack overflow answer。 否则,您的 VerseDao.findById 函数中的 fetch 可能每次都会扫描整个数据库表。 要查看您的索引是否正常工作,您可以检查通过将-com.apple.CoreData.SQLDebug 1 添加到 Xcode 方案中的启动参数生成的 SQL 查询。

其他改进:

  • 使用NSManagedObjectContext.fetchNSFetchRequest.execute(等效)代替NSFetchedResultsController。 NSFetchedResultsController 通常用于将结果绑定到 UI。在这种情况下,使用它只会增加开销。
  • 不要设置fetchRequest.propertiesToFetch,而是设置fetchRequest.includesPropertyValues = false。这将避免获取不需要与 Translation 对象建立关系的 Verse 对象属性值。
  • 不要在获取请求上指定 sortDescriptor,这只会使查询复杂化

【讨论】:

  • 感谢您的回答。我已经有了索引并进行了您建议的其他更改。它似乎仍然很慢。我想知道是否有一种方法可以在不调用 coredata 数据库的情况下使用 verseID 创建 Verse 对象?我只需要一个参考。在普通的 RDBM 中,它只是在第二个表中插入关联的主键。
  • 你有时间判断它有多慢,以便我们判断是否正常?没有您提到的查找方式,您无法创建关系,Core Data 是一个对象图,而不是关系数据库。您是否需要更新您的数据(例如从 Web 服务),还是只在首次应用启动时从 json 文件中导入一次?如果只有一次,您可以考虑将预建商店作为应用程序资源发布,请参阅developer.apple.com/library/archive/documentation/Cocoa/…
  • 在 iPhone 7 上需要 1 分 40 秒。对于第二部分,我需要从 Web 服务下载,而不是一次性下载。
  • 对于 7k 查找和插入来说,听起来比预期的要慢。由于您在上面说大部分时间都花在 Verse 获取请求上,我建议您在 sqlite 中为该查询执行 EXPLAIN QUERY PLAN(您可以使用 -com.apple.CoreData.SQLDebug 1 查看查询)。本文解释了如何进行 EXPLAIN:objc.io/issues/4-core-data/core-data-fetch-requests
猜你喜欢
  • 2016-04-12
  • 1970-01-01
  • 2016-07-14
  • 1970-01-01
  • 2017-01-07
  • 2013-08-03
  • 1970-01-01
  • 2013-05-12
相关资源
最近更新 更多