繁体   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