簡體   English   中英

CoreData並發問題

[英]CoreData Concurrency issue

使用private managedObjectContext在后台保存數據時出現問題。 我是CoreData的新手。 我正在為NSManagedObjectContext使用Parent-Child方法,但是遇到了幾個問題。

當我多次點擊重新加載按鈕時出現錯誤

錯誤:

  1. 'NSGenericException',原因:枚舉時集合<__ NSCFSet:0x16e47100>發生了變異

  2. 有時:在此處崩潰請try managedObjectContext.save()

  3. 有時鍵值編碼符合錯誤

我的ViewController類

        class ViewController: UIViewController {
            var jsonObj:NSDictionary?
            var values = [AnyObject]()
            @IBOutlet weak var tableView:UITableView!

            override func viewDidLoad() {
                super.viewDidLoad()
                getData()
                saveInBD()
                NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(self.saved(_:)), name: "kContextSavedNotification", object: nil)
            }
   //Loding json data from a json file

           func getData(){
            if let path = NSBundle.mainBundle().pathForResource("countries", ofType: "json") {
            do {
            let data = try NSData(contentsOfURL: NSURL(fileURLWithPath: path), options: NSDataReadingOptions.DataReadingMappedIfSafe)


            do {
            jsonObj =  try NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers) as? NSDictionary

            } catch {
            jsonObj = nil;
            }


            } catch let error as NSError {
            print(error.localizedDescription)
            }
            } else {
            print("Invalid filename/path.")
            }
            }
           **Notification reciever**

            func saved(not:NSNotification){
                dispatch_async(dispatch_get_main_queue()) {
                    if let data  = DatabaseManager.sharedInstance.getAllNews(){
                        self.values = data
                        print(data.count)
                        self.tableView.reloadData()

                    }

                }
                }

            func saveInBD(){
                if jsonObj != nil {
                    guard let nameArray = jsonObj?["data#"] as? NSArray else{return}
                    DatabaseManager.sharedInstance.addNewsInBackGround(nameArray)
                }
            }
            //UIButton for re-saving data again

            @IBAction func reloadAxn(sender: UIButton) {
                saveInBD()
            }

    }


    **Database Manager Class**

    public class DatabaseManager{

        static  let sharedInstance = DatabaseManager()

        let managedObjectContext = (UIApplication.sharedApplication().delegate as! AppDelegate).managedObjectContext

        private init() {
        }

        func addNewsInBackGround(arr:NSArray)  {
            let jsonArray = arr
            let moc = managedObjectContext

            let privateMOC = NSManagedObjectContext(concurrencyType: .PrivateQueueConcurrencyType)
            privateMOC.parentContext = moc

                privateMOC.performBlock {
                    for jsonObject in jsonArray {
                        let entity =  NSEntityDescription.entityForName("Country",
                            inManagedObjectContext:privateMOC)

                        let managedObject = NSManagedObject(entity: entity!,
                            insertIntoManagedObjectContext: privateMOC) as! Country

                        managedObject.name = jsonObject.objectForKey("name")as? String

                    }


                    do {
                        try privateMOC.save()

                        self.saveMainContext()

                        NSNotificationCenter.defaultCenter().postNotificationName("kContextSavedNotification", object: nil)
                    } catch {
                        fatalError("Failure to save context: \(error)")
                    }
                }

        }





        func getAllNews()->([AnyObject]?){
            let fetchRequest = NSFetchRequest(entityName: "Country")
            fetchRequest.resultType = NSFetchRequestResultType.DictionaryResultType

            do {
                let results =
                    try managedObjectContext.executeFetchRequest(fetchRequest)
                results as? [NSDictionary]
                if results.count > 0
                {
                    return results
                }else
                {
                    return nil
                }
            } catch let error as NSError {
                print("Could not fetch \(error), \(error.userInfo)")
                return nil
            }
        }

        func saveMainContext () {
            if managedObjectContext.hasChanges {
                do {
                    try managedObjectContext.save()
                } catch {
                    let nserror = error as NSError
                    print("Unresolved error \(nserror), \(nserror.userInfo)")
                }
            }
        }
    }

可以在后台編寫並在主線程中進行讀取(就像您一樣使用不同的MOC)。 實際上,您幾乎在做對了。

該應用程序在saveMainContext try managedObjectContext.save()行上崩潰,因為在私有MOC的performBlock內部調用了saveMainContext 修復它的最簡單方法是將保存操作包裝到另一個performBlock

func saveMainContext () {
    managedObjectContext.performBlock {
        if managedObjectContext.hasChanges {
            do {
                try managedObjectContext.save()
            } catch {
                let nserror = error as NSError
                print("Unresolved error \(nserror), \(nserror.userInfo)")
            }
        }
    }
}

其他兩個錯誤則比較棘手。 請提供更多信息。 哪個對象的鍵值不符合鍵值? 這很可能是JSON解析問題。

第一個錯誤(“在枚舉時發生變異”)實際上是一個令人討厭的錯誤。 描述非常簡單:一個線程對一個集合進行了變異,而另一個線程對其進行了枚舉。 它發生在哪里? 一個可能的原因(很可能是我說的一個原因)是,這確實是Core Data多線程問題。 盡管您可以使用多個線程,但是您只能在獲取它們的線程內使用核心數據對象。 如果將它們傳遞給另一個線程,則可能會遇到這樣的錯誤。

查看您的代碼並嘗試查找可能發生這種情況的位置(例如,您self.values從其他類訪問self.values ?)。 不幸的是,我在幾分鍾內找不到這樣的地方。 如果您說哪個集合枚舉發生此錯誤,它將有所幫助)。

更新: PS我只是認為該錯誤可能與saveMainContext函數有關。 它是在調用saved之前執行的。 saveMainContext在后台線程上執行(我的意思是在原始代碼中),而saved在主線程上執行。 因此,在修復saveMainContext ,錯誤可能會消失(但是我不確定100%)。

您違反了線程限制。

您不能在后台寫入CoreData,也不能在MainThread中讀取。

CoreData上的所有操作必須在同一線程中完成

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM