coredata更新json后台线程问题

Posted

技术标签:

【中文标题】coredata更新json后台线程问题【英文标题】:coredata update json background thread problem 【发布时间】:2020-10-08 16:48:58 【问题描述】:

我有以下 coredata 单例类 在我的 appdelegate 中,我尝试从 json 更新数据 但是在我的应用程序启动时,我收到有关线程的不同错误消息 喜欢

主线程检查器:调用 UI API 或 NSManagedObjectContextObjectsDidChangeNotification 的观察者 或不正确的保护值

有什么问题,我应该做些什么改变才能让它工作? 谢谢

import CoreData
import Foundation
class CoreDataStack 
   
  static let sharedManager = CoreDataStack()
  private init()  // Prevent clients from creating another instance.
   
  //This is the name of your coreData Database
  static let modelName = "myDB"
  static let FirstLaunchKey = "firstLaunch"
 
  lazy var managedObjectModel: NSManagedObjectModel = 
    let modelURL = Bundle.main.url(forResource: CoreDataStack.modelName, withExtension: "momd")!
    return NSManagedObjectModel(contentsOf: modelURL)!
  ()
   
   
  lazy var applicationDocumentsDirectory: URL = 
    return NSPersistentContainer.defaultDirectoryURL()
  ()
   
  lazy var persistentStoreCoordinator: NSPersistentStoreCoordinator = 
    let coordinator = NSPersistentStoreCoordinator(managedObjectModel: self.managedObjectModel)
     
    let persistentStoreURL = self.applicationDocumentsDirectory.appendingPathComponent(CoreDataStack.modelName + ".sqlite")
     
    do 
       
      // let dict = [NSSQLitePragmasOption: ["journal_mode":"DELETE"]]
  try coordinator.addPersistentStore(ofType: NSSQLiteStoreType,
                    configurationName: nil,
                    at: persistentStoreURL,
              options: [NSMigratePersistentStoresAutomaticallyOption: true,
                   NSInferMappingModelAutomaticallyOption: false])
     catch 
      fatalError("Persistent store error! \(error)")
    
     
    return coordinator
  ()
   
   
  fileprivate lazy var saveManagedObjectContext: NSManagedObjectContext = 
    let moc = NSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType)
    moc.persistentStoreCoordinator = self.persistentStoreCoordinator
    return moc
  ()
   
  @objc lazy var mainObjectContext: NSManagedObjectContext = 
    let managedObjectContext = NSManagedObjectContext(concurrencyType: .mainQueueConcurrencyType)
    managedObjectContext.parent = self.saveManagedObjectContext
    return managedObjectContext
  ()
   
  func saveMainContext() 
    guard mainObjectContext.hasChanges || saveManagedObjectContext.hasChanges else 
      return
    
 
    mainObjectContext.performAndWait() 
      // print("save performAndWait")
      do 
        try self.mainObjectContext.save()
       catch 
        fatalError("Error saving main managed object context! \(error)")
      
    
    saveManagedObjectContext.perform() 
      // print("save simple")
      do 
        try self.saveManagedObjectContext.save()
       catch 
        fatalError("Error saving private managed object context! \(error)")
      
    
  
  func removeDatabaseForVersion(version:String)
    let previouslyVersion = UserDefaults.standard.bool(forKey: version)
     if !previouslyVersion 
      UserDefaults.standard.set(true, forKey: version)
      // Default directory where the CoreDataStack will store its files
      let directory = NSPersistentContainer.defaultDirectoryURL()
      let url = directory.appendingPathComponent(CoreDataStack.modelName + ".sqlite")
       
      let shmURL = directory.appendingPathComponent(CoreDataStack.modelName + ".sqlite-shm")
       
      let walURL = directory.appendingPathComponent(CoreDataStack.modelName + ".sqlite-wal")
       
      _ = try? FileManager.default.removeItem(at: url)
      _ = try? FileManager.default.removeItem(at: shmURL)
      _ = try? FileManager.default.removeItem(at: walURL)
    
  
   

在我的 appdelegate 中:

UpdateDbClass.updateDatabase(entityName: DbTable.VehiclesEntity.rawValue, completionHandler: 
      print(" DB updated delegate")
    )

在更新的b类中:

import UIKit
import Alamofire
import CoreData


enum LoaderError:String
    case
    JsonFailed,
    PathFailed,
    NoEntityDescription,
    UnknownError


enum DbTable:String
    case
    VehiclesEntity,
    PhotosEntity,
    ModelsEntity,
    NewsEntity,
    StylesEntity


class UpdateDbClass 
    
    
    static func updateDatabase(entityName:String,completionHandler: @escaping () -> Void)
      var url = URL(string: UrlRepository.VehiclesJsonUrl!)!
      
   
      var table = ""
          switch entityName 
          case DbTable.VehiclesEntity.rawValue:
              table = "Vehicles"
              url = URL(string: UrlRepository.VehiclesJsonUrl!)!
          case DbTable.PhotosEntity.rawValue:
              table = "Photos"
              url = URL(string: UrlRepository.PhotosJsonUrl!)!
               table = "Styles"
                url = Bundle.main.url(forResource: "Styles", withExtension: "json")!
                  // url = URL(string: UrlRepository.NewsJsonUrl!)!
          default:
              break
          
          let uuid = UUID().uuidString
          let parameters: Parameters = [
              "id": uuid
          ]

     let queue = DispatchQueue(label: "com.my.test", qos: .background, attributes: .concurrent)
    

      AF.request(url, method: .get, parameters: parameters, encoding: URLEncoding(destination: .queryString), headers: nil).responseJSON(queue:queue) response in
              switch response.result 
              case let .success(value):
                  if let items = value as? [[String: Any]] 

                      var itemsArray:[Int32] = []
                      for item in items
                          if let id = item["id"] as? Int32
                               itemsArray.append(id)
                          
                      

                      guard let entity = NSEntityDescription.entity(forEntityName: table, in:(CoreDataStack.sharedManager.mainObjectContext)) else 
                          fatalError("Could not find entity descriptions!")
                      

              switch entityName 

                  case DbTable.StylesEntity.rawValue: //Styles
                      checkDeletedRecords(jsonItems: itemsArray,table: table)

                      for item in items
                          guard let id = item["id"] as? Int32 else return
                          //Check if not exists
                          if  !CheckIfExists(id: id,table:table)
                             print("id \(id) does not exist")
                              //Insert Record
                              let object = Styles(entity: entity, insertInto: CoreDataStack.sharedManager.mainObjectContext)
                      object.setValue(item["id"], forKey: "id")
                      object.setValue(item["style"] as! String, forKey: "style")
                      object.setValue(item["image"] as! String, forKey: "image")
         
                      CoreDataStack.sharedManager.saveMainContext()
                          
                          else    //Update Record
                             // print("id \(item["id"]) exists")

                              do
                                  let fetchRequest = NSFetchRequest<Styles>(entityName:"Styles")
                                  let predicate = NSPredicate(format: "id == %d",item["id"] as! Int32)
                                  fetchRequest.predicate = predicate
                                  let req =  try CoreDataStack.sharedManager.mainObjectContext.fetch(fetchRequest)
                                  let object = req[0] as NSManagedObject
                                object.setValue(item["style"] as! String, forKey: "style")
                                 object.setValue(item["image"] as! String, forKey: "image")
                                

                                   CoreDataStack.sharedManager.saveMainContext()

                              catch
                                  print("there was an error")
                              
                              completionHandler()
                          
                      
                      break;

             
                      default:
                          break
                      
                   
                  break
              case let .failure(error):
                  print(error as NSError)
                  break
              
          
      

       
      


  


protocol CoreDataWorkerProtocol 
    associatedtype EntityType


enum VoidResult 
    case success
    case failure(NSError)


【问题讨论】:

错误信息提到了NSManagedObjectContextObjectsDidChangeNotification的观察者。你有任何观察这个通知的代码吗?如果是这样,观察代码会发生什么? 不,我没有观察者! 我现在收到此错误:主线程检查器:在后台线程上调用 UI API:-[UIViewController childViewControllers] PID:2441,TID:136254,线程名称: 【参考方案1】:

响应 json 在后台线程中被调用,您尝试在后台线程中使用 viewContext

如果你想在后台使用 viewContext,你应该使用 perform 块。

比如这样

  AF.request(:).responseJSON(queue:queue) response in
       let mainContext = CoreDataStack.sharedManager.mainObjectContext
        mainContext.perform  
        guard let entity = NSEntityDescription.entity(forEntityName: table, in: mainContext) else 
                fatalError("Could not find entity descriptions!")
                
        
   

【讨论】:

以上是关于coredata更新json后台线程问题的主要内容,如果未能解决你的问题,请参考以下文章

在 CoreData 的后台线程中创建实体

延迟后在后台线程中处理核心数据

在后台线程中加载 CoreData

核心数据-后台线程中的更新实体会自动更改主线程中的 NSManagedObject 而无需合并-为啥?

使用 Web 服务获取 CoreData 背景?

在 cellForRowAtIndexPath 中获取 iOS CoreData 后台线程