在后台线程问题中将服务器数据保存到核心数据

Posted

技术标签:

【中文标题】在后台线程问题中将服务器数据保存到核心数据【英文标题】:Saving server data to core data, in background thread issues 【发布时间】:2016-12-08 09:23:50 【问题描述】:

我知道并发真的很头疼。我看了很多关于如何解析服务器数据并将其保存到核心数据的文章。但是很多教程非常基础,它们不适用于线程。但是当我们开发一个应用程序时,需要多线程。

我的应用流程如下

1.使用 URLSession 从服务器获取数据 2.并解析 3.保存到核心数据 - 在后台,需要检查重复 4.然后获取数据 5.更新用户界面。

卡在第三步 保存到核心数据 - 在后台,需要检查重复项

我读到了,URLSession 正在后台线程中运行。需要像苹果说的那样使用performblock吗?https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/CoreData/Concurrency.html

我尝试了以下方式。但有时它会崩溃。最好有人建议正确的编程流程。关于这个有很多问题,但对于这个问题没有正确的解决方案。

func DownloadFrom_server(dict:Any,urlstring_get:String)  
    if JSONSerialization.isValidJSONObject(dict) 
        do
            let json:Data = try JSONSerialization.data(withJSONObject: dict, options: JSONSerialization.WritingOptions.prettyPrinted)

            var urlString = urlstring_get
            urlString =   urlString.addingPercentEncoding(withAllowedCharacters: CharacterSet.urlPathAllowed)!
            var httpRequest = URLRequest(url: URL(string: urlString)!)
            httpRequest.httpMethod = "POST"
            httpRequest.httpBody = json
            let sessionConfig = URLSessionConfiguration.default
            sessionConfig.httpAdditionalHeaders = ["Accept" : "application/json", "api-key" : "API_KEY"]
            let session = URLSession(configuration: sessionConfig)
            let task = session.dataTask(with: httpRequest)  data, response, error in
                guard let data_get = data, error == nil else// check for fundamental networking error
                    print("data nil\(error)")
                    //1.network connection lost
                    //save offline data
                    return
                
                do 
                    let jsonResult = try JSONSerialization.jsonObject(with: data_get, options: JSONSerialization.ReadingOptions.mutableContainers)
                    print("cloud data",jsonResult)
                    self.Save_server_Data_ToCoreData(jsonResult: jsonResult)
                
                catch 
                    print("json result parsing error ",error)
                  
            
            task.resume()
           
        catch 
            print("InValidJSONObject json:Data error - ",error)
        
    

并将服务器josn保存到核心数据如下

func Save_server_Data_ToCoreData(jsonResult:Any)  
    let context = getContext()
    let privateMOC = NSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType)
    privateMOC.parent = context
    privateMOC.perform 
        if let data = jsonResult  as? [[String: AnyObject]]
            //Heree prepare MOC
            // if i need to check data alredy exits or not means how can i check
        
        do 
            try privateMOC.save()
          context.performAndWait 
                do 
                    try context.save()
                 catch 
                    fatalError("Failure to save context: \(error)")
                
            
         catch 
            fatalError("Failure to save context: \(error)")
        
   

   DispatchQueue.main.async(execute: 
                        print("update main")
                    )

我又想按照以下方式做

             DispatchQueue.global(qos: .background).async 
//saving core data
 //fetching data
DispatchQueue.main.async 
   //update UI

     

不确定需要使用哪个上下文,private 还是 main。如果回答这个问题的人对像我这样的多线程初学者有用的话。

【问题讨论】:

试试 MagicalRecord : github.com/magicalpanda/MagicalRecord 【参考方案1】:

对于核心数据的重复性检查。 ios9以上Coredata有一个独特的约束功能。

在实体描述的约束中设置属性名称,如 id、name 等

并在 managedobjectcontext 中将合并策略设置为 NSMergeByPropertyObjectTrumpMergePolicy

managedObjectContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy

请记住,如果您的实体中有关系(我在 xcode7.3 中检查过),上述重复性检查功能将不起作用。

【讨论】:

【参考方案2】:

您可以通过唯一字段(例如id)检查或创建新功能。如果假设记录已经存在,则可以跳过或更新字段,但不能创建新记录。 (确保您的实体具有唯一的主键字段)

您可以在循环从服务器 API 接收到的数据时调用它

func findOrCreate(byId objectId: String) -> NSManagedObject 
    let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "Document")

    // Create a new predicate that filters out any object that equal to this id
    print("looking for id: \(objectId)")

    let predicate = NSPredicate(format: "objectId == %@", objectId)

    // Set the predicate on the fetch request
    fetchRequest.predicate = predicate

    do 

        // if found in core data
        let results = try managedObjectContext.fetch(fetchRequest)
        if !results.isEmpty 
            return results.first as! NSManagedObject
        

     catch let error as NSError 
        print("Could not create object \(error), \(error.userInfo)")
    

    // else create new object
    let entity =  NSEntityDescription.entity(forEntityName: "DocumentEntity", in:managedObjectContext)

    let newObject = NSManagedObject(entity: entity!, insertInto: managedObjectContext)

    return newObject

【讨论】:

谢谢。但我说的是多线程。不是为了检查重复。

以上是关于在后台线程问题中将服务器数据保存到核心数据的主要内容,如果未能解决你的问题,请参考以下文章

在后台线程中将核心数据从一个数据库复制到另一个数据库

iOS/Objective-C:在后台线程中保存到核心数据

从后台线程保存数据时核心数据崩溃

是否自动在后台线程中将数据发布到 api

删除核心数据对象并保存在后台线程中

核心数据和后台线程的问题