2016-09-15 112 views
2

我工作的現有項目,使用CoreData,即:CoreData阻塞UI

把從不同CoreData實體類型很多很多的項目,從網絡服務接收它們之後,卻塊多秒UI線程,即使我在另一個線程中使用它。

請幫助我,有沒有什麼辦法可以防止CoreData以最小的變化阻止UI,因爲項目幾乎完成了?

我是CoreData的新手,不幸的是我沒有足夠的時間研究文檔或重新編寫源代碼。

我DataController類:

class DataController { 

var managedObjectContext: NSManagedObjectContext 
let modelName = "something" 


init(closure:()->()) { 

    guard let modelURL = NSBundle.mainBundle().URLForResource(modelName, withExtension: "momd"), 
     let managedObjectModel = NSManagedObjectModel.init(contentsOfURL: modelURL) 
     else { 
      fatalError("DataController - COULD NOT INIT MANAGED OBJECT MODEL") 
    } 

    let coordinator = NSPersistentStoreCoordinator.init(managedObjectModel: managedObjectModel) 

    managedObjectContext = { 
     let parentContext = NSManagedObjectContext(concurrencyType: .PrivateQueueConcurrencyType) 
     parentContext.persistentStoreCoordinator = coordinator 

     let managedObjectContext = NSManagedObjectContext(concurrencyType: .MainQueueConcurrencyType) 
     managedObjectContext.parentContext = parentContext 
     return managedObjectContext 
    }() 

    dispatch_async(dispatch_get_global_queue(Int(QOS_CLASS_USER_INITIATED.rawValue), 0)) { 

     let options = [ 
      NSMigratePersistentStoresAutomaticallyOption: true, 
      NSInferMappingModelAutomaticallyOption: true, 
      NSSQLitePragmasOption: ["journal_mode": "DELETE"] 
     ] 

     let documentsURL = NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask).last 
     let storeURL = NSURL.init(string: "something", relativeToURL: documentsURL) 


     do { 
      try coordinator.addPersistentStoreWithType(NSSQLiteStoreType, configuration: nil, URL: storeURL, options: options) 

      dispatch_async(dispatch_get_main_queue()) { 
       closure() 
      } 
     } 
     catch let error as NSError { 
      fatalError("DataController - COULD NOT INIT SQLITE STORE: \(error.localizedDescription)") 
     } 
    } 
} 

func save(moc: NSManagedObjectContext? = nil) { 

    var managedObjectContext = moc 

    if moc == nil { 
     managedObjectContext = self.managedObjectContext 
    } 

    managedObjectContext!.performBlockAndWait { 

     if managedObjectContext!.hasChanges { 

      do { 
       try managedObjectContext!.save() 
      } catch { 
       print("ERROR saving context \(managedObjectContext!.description) - \(error)") 
      } 
     } 

     if let parentContext = managedObjectContext!.parentContext { 
      self.save(parentContext) 
     } 
    } 
} 


} 

我的班是一樣的東西:

class MOCity: NSManagedObject { 

@NSManaged var id: NSNumber? 
@NSManaged var name: String? 

// Insert code here to add functionality to your managed object subclass 
class func save(id: NSNumber, json: JSON, managedObjectContext: NSManagedObjectContext) { 

    guard let newObject = MOCity.getById(id, create: true, managedObjectContext: managedObjectContext) else { 
     fatalError("City - NO OBJECT") 
    } 

    newObject.id    <-- json["Id"] 
    newObject.name    <-- json["Name"] 

} 

internal class func getById(id: NSNumber, create: Bool = false, managedObjectContext: NSManagedObjectContext) -> MOCity? { 

    let fetchRequest = NSFetchRequest(entityName: "City") 
    fetchRequest.predicate = NSPredicate(format: "id = \(id)") 

    do { 
     guard let fetchResults = try managedObjectContext.executeFetchRequest(fetchRequest) as? [MOCity] else { 
      fatalError("city - NO FETCH RESULTS") 
     } 

     if fetchResults.count > 0 { 
      return fetchResults.last 
     } 
     else if fetchResults.count == 0 { 

      if create { 
       guard let object = NSEntityDescription.insertNewObjectForEntityForName("City", inManagedObjectContext: managedObjectContext) as? MOCity else { 
        fatalError("getCity - COULD NOT CREATE OBJECT") 
       } 
       return object 
      } else { 
       return nil 
      } 
     } 
    } 
    catch let error as NSError { 
    ... 
    } 

    return nil 
} 
} 

我的Web服務類:

Alamofire.request(.POST, url,parameters:data).validate().responseJSON(completionHandler: {response in 

     switch response.result { 

     case .Success: 

      if let jsonData = response.result.value { 

       if let array = jsonData as? NSArray { 

       for arrayItem in array { 
        MOCity.save(arrayItem["Id"] as! NSNumber, json: JSON(arrayItem as! NSDictionary)!, managedObjectContext: self.dataController.managedObjectContext) 
       } 

       self.dataController.save() 

       } 

      } 
      break 
     case .Failure(let error): 
     ...     
     } 


    }) 

回答

2

問題的一個可以連接的QOS水平。

QOS_CLASS_USER_INITIATED:用戶啓動的類代表從UI啓動的任務 ,可以異步執行。當用戶等待立即結果時應使用它 ,並且需要 任務以繼續用戶交互。

嘗試使用QOS_CLASS_BACKGROUND

+0

我試過使用它,但是它在覈心數據操作中造成了崩潰。任何解決方案或示例代碼請? –

+1

嗯,我會調查你的代碼更多。 –

+0

謝謝,我會等你的幫助。 :) –

1

我會建議兩件事。首先,像執行主隊列上下文一樣,將專用隊列上下文保存在performBlock函數中。其次,在保存之前和之後的不同位置設置斷點,以檢查哪個線程正在調用該代碼(使用NSThread的線程檢查方法)。這可能會啓發你從哪個線程執行哪些代碼。希望能夠幫助你指引正確的方向。

+0

我會檢查,謝謝 –