Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/swift/20.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Ios 使用核心数据插入/更新记录的最有效方法?_Ios_Swift_Core Data - Fatal编程技术网

Ios 使用核心数据插入/更新记录的最有效方法?

Ios 使用核心数据插入/更新记录的最有效方法?,ios,swift,core-data,Ios,Swift,Core Data,我正在开发一个定期从服务器下载数据的应用程序。如果数据需要更新,我会使用下面的方法来更新记录,或者在不存在的情况下插入新记录 let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "Trip") for csvTrip in csvTrips { var trip: NSManagedObject! let tripId = Int(csvTrip[0])!

我正在开发一个定期从服务器下载数据的应用程序。如果数据需要更新,我会使用下面的方法来更新记录,或者在不存在的情况下插入新记录

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
    }
let fetchRequest=NSFetchRequest(entityName:“Trip”)
对于csvTrips中的csvTrip{
var trip:NSManagedObject!
让tripId=Int(csvTrip[0])!
fetchRequest.predicate=NSPredicate(格式:“id=%d”,tripId)
if(context.count(for:fetch)==0){
trip=NSEntityDescription.insertNewObject(forEntityName:“trip”,插入:上下文)
trip.setValue(tripId,forKey:“id”)
}否则{
tripObject=(context.fetch(fetch)as![NSManagedObject])[0]
}
//设置其他属性
}

检查每个循环中是否已经存在实体比不检查就插入实体要慢100倍,这对于数千个实体来说是一个大问题。我已经尝试首先获取所有的实体,但是我仍然必须循环每个实体,并将id添加到数组或其他东西中,这不会更快。我知道核心数据和MySQL不一样,但我很难相信没有类似于INSERT的功能。。。重复密钥更新,这在MYSQL中非常快。我错过什么了吗

加快插入/更新速度的一种方法是将输入数组分割成相当小的“bucket”,并在NSPredicate中使用
中的
操作符。使用
中的
操作符,您可以通过单个查询检查存储桶的所有元素是否已经存在于数据库中。让我用一些代码来说明这一点

let bucketSize = 10

let bucketStart = 0
let bucketEnd = bucketSize

while bucketStart < csvTrips.count {
    let tripBucket = csvTrips[bucketStart..<bucketEnd]

    let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "Trip")
    fetchRequest.predicate = NSPredicate(format: "id in %@", tripBucket.map {Int($0[0])})

    // count == bucketSize would imply that all elements in the bucket are also in the db, in which case we simply move on to the next bucket
    if context.count(for: fetch) != bucketSize {
        // some of the elements in the bucket are not in the db,
        // now use your existing code to update the missing ones
        for csvTrip in tripBucket {
            // ...
        }
    }

    // update bucketStart and bucketEnd here
}

bucket大小过大意味着几乎所有的bucket都将至少有一个元素从数据库中丢失;这反过来又意味着,与现有方法相比,您几乎没有优势。另一方面,bucket大小太小意味着额外获取请求(
id in%@
)的开销太大。

如果获取几千个实体并将id加载到
集中花费了特别长的时间,我会感到惊讶

您可以使用以下内容:

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")
 }
在我的测试中,将320000个行程id加载到一个集合中需要1.35秒,创建10000个新行程需要0.08秒,同时检查行程id是否包含在集合中。

您可以使用核心数据的独特约束技术

  • 告诉核心数据您的id是唯一标识符。 为此,请选择您的数据模型(Trip.xcdatamodeld),并确保选择了Trip实体,而不是它的一个属性。 在数据模型检查器中,查看“约束”字段,然后单击该字段底部的+按钮。 将出现一个新行,显示“逗号、分隔、属性”。单击该按钮,按Enter键使其可编辑,然后键入id并再次按Enter键。按Cmd+S保存更改
  • 修改
    loadPersistentStores()
    方法调用以允许核心数据更新对象:
  • 注意: 使用属性约束可能会导致
    NSFetchedResultsController
    出现问题:属性约束仅在保存时强制为唯一,这意味着如果插入数据,则
    NSFetchedResultsController
    可能包含重复项,直到发生保存。您可以通过在加载之前执行保存来避免这种情况。要知道,做出这样的改变是你的责任


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

    获取所有ID并将其加载到
    集合中应该非常快。有没有方法专门获取ID?我知道的唯一方法是获取每个对象并循环每个对象以将它们添加到集合中,这在我尝试时并没有更快。您可以将获取请求的
    propertiesToFetch
    属性设置为仅返回
    id
    。还将结果类型设置为
    字典ResultType
    。然后,您可以获取所有当前对象,并使用
    map
    操作快速将ID加载到集合中。如果您想编写关于唯一约束的答案,请这样做,但仅链接的答案会过时并更改。@WarrenBurton我已更新了一个答案,其中包含详细信息,如果有人对该理论感兴趣,请保留链接
    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")
     }
    
    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")
            }
         }
    }
    
    container.loadPersistentStores { storeDescription, error in
        self.container.viewContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy
    
        if let error = error {
            print("Unresolved error \(error)")
        }
    }