使用Core Data插入/更新记录的最有效方法?

Bre*_*ley 2 core-data ios swift

我正在开发一个定期从服务器下载数据的应用程序.如果数据需要更新,我会使用类似的内容来更新记录或插入新记录(如果它们不存在).

let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "Trip")
    for csvTrip in csvTrips {
        var trip: NSManagedObject!

        let tripId = Int(csvTrip[0])!
        fetchRequest.predicate = NSPredicate(format: "id = %d", tripId)

        if (context.count(for: fetch) == 0) {
            trip = NSEntityDescription.insertNewObject(forEntityName: "Trip", into: context)
            trip.setValue(tripId, forKey: "id")
        } else {
            tripObject = (context.fetch(fetch) as! [NSManagedObject])[0]
        }

        // Set other properties
    }
Run Code Online (Sandbox Code Playgroud)

检查实体是否已存在于每个循环中使得它比仅插入它们大约慢100倍而不进行检查,这对于超过几千个实体来说变成了一个大问题.我已经尝试首先获取所有实体,但我仍然必须循环遍历每个实体并将id添加到数组或其他东西,这不是更快.我知道核心数据与MySQL不同,但我很难相信没有类似INSERT ...... ON DUPLICATE KEY UPDATE的功能,这在MYSQL中非常快.我错过了什么吗?

Lio*_*ion 12

您可以使用 Core Data独特的约束技术。

  1. 告诉 Core Data 您的id是唯一标识符。为此,请选择您的数据模型 (Trip.xcdatamodeld) 并确保选择 Trip 实体而不是其属性之一。在数据模型检查器中,查看“约束”字段,然后单击该字段底部的 + 按钮。将出现一个新行,显示“逗号,分隔,属性”。单击该按钮,按 Enter 键使其可编辑,然后输入id并再次按 Enter 键。按 Cmd+S 保存更改。

在此输入图像描述

  1. 修改loadPersistentStores()方法调用以允许 Core Data 更新您的对象:
container.loadPersistentStores { storeDescription, error in
    self.container.viewContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy

    if let error = error {
        print("Unresolved error \(error)")
    }
}
Run Code Online (Sandbox Code Playgroud)

注意:使用属性约束可能会导致问题NSFetchedResultsController:属性约束仅在保存时强制执行为唯一,这意味着如果您要插入数据,则NSFetchedResultsController在保存之前可能会包含重复项。您可以通过在加载之前执行保存来避免这种情况。只需要知道,做出这样的改变取决于你。

您可以在此处阅读有关此技术的更多信息。


Pau*_*w11 7

如果取出几千个实体并将ID加载到Set一个特定的很长时间,我会感到惊讶.

你可以使用类似的东西:

let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "Trip")
fetchRequest.resultType = .dictionaryResultType
fetchRequest.propertiesToFetch = ["id"]
do {
   if let results = try self.moc.fetch(fetchRequest) as? [[String:Any]] {
       let idSet = Set<Int32>(results.flatMap({ (dict) -> Int32? in
                return dict["id"] as? Int32
        }))
   }
 } catch {
     print("Error reading trips")
 }
Run Code Online (Sandbox Code Playgroud)

现在,您可以轻松检查给定的ID是否是新的,并在需要时插入新的行程:

for csvTrip in csvTrips {
    if let tripId = Int(csvTrip[0]) {
        if !idSet.contains(tripId) {
            trip = NSEntityDescription.insertNewObject(forEntityName: "Trip", into: context)
            trip.setValue(tripId, forKey: "id")
        }
     }
}
Run Code Online (Sandbox Code Playgroud)

在我的测试中,这需要1.35秒来将320,000次行程ID加载到一个集合中,并且需要0.08秒来创建10,000次新行程,同时检查行程ID是否包含在集合中.