简体   繁体   English

CoreData并发问题

[英]CoreData Concurrency issue

I am having issue while using private managedObjectContext for saving data in background. 使用private managedObjectContext在后台保存数据时出现问题。 I am new to CoreData. 我是CoreData的新手。 I am using Parent-Child approach for NSManagedObjectContext but facing several issues. 我正在为NSManagedObjectContext使用Parent-Child方法,但是遇到了几个问题。

Errors arise when I tap reload button multiple times 当我多次点击重新加载按钮时出现错误

Errors: 错误:

  1. 'NSGenericException', reason: Collection <__NSCFSet: 0x16e47100> was mutated while being enumerated 'NSGenericException',原因:枚举时集合<__ NSCFSet:0x16e47100>发生了变异

  2. Some times : crash here try managedObjectContext.save() 有时:在此处崩溃请try managedObjectContext.save()

  3. Sometimes Key value coding Compliant error 有时键值编码符合错误

My ViewController class 我的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)")
                }
            }
        }
    }

You can write in background and read in the main thread (using different MOCs like you do). 可以在后台编写并在主线程中进行读取(就像您一样使用不同的MOC)。 And actually you're almost doing it right. 实际上,您几乎在做对了。

The app crashes on the try managedObjectContext.save() line, because saveMainContext is called from within the private MOC's performBlock. 该应用程序在saveMainContext try managedObjectContext.save()行上崩溃,因为在私有MOC的performBlock内部调用了saveMainContext The easiest way to fix it is to wrap the save operation into another performBlock : 修复它的最简单方法是将保存操作包装到另一个performBlock

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

Other two errors are a little more tricky. 其他两个错误则比较棘手。 Please, provide more info. 请提供更多信息。 What object is not key-value compliant for what key? 哪个对象的键值不符合键值? It's most likely a JSON parsing issue. 这很可能是JSON解析问题。

The first error ("mutated while being enumerated") is actually a nasty one. 第一个错误(“在枚举时发生变异”)实际上是一个令人讨厌的错误。 The description is pretty straight forward: a collection was mutated by one thread while it was enumerated on the other. 描述非常简单:一个线程对一个集合进行了变异,而另一个线程对其进行了枚举。 Where does it occur? 它发生在哪里? One possible reason (most likely one, I would say) is that it is indeed a Core Data multithreading issue. 一个可能的原因(很可能是我说的一个原因)是,这确实是Core Data多线程问题。 Despite the fact that you can use several threads, you can only use core data objects within the thread they were obtained on. 尽管您可以使用多个线程,但是您只能在获取它们的线程内使用核心数据对象。 If you pass them to another thread, you'll likely run into an error like this. 如果将它们传递给另一个线程,则可能会遇到这样的错误。

Look through your code and try to find a place where such situation might occur (for instance, do you access self.values from other classes?). 查看您的代码并尝试查找可能发生这种情况的位置(例如,您self.values从其他类访问self.values ?)。 Unfortunately, I wasn't able to find such place in several minutes. 不幸的是,我在几分钟内找不到这样的地方。 If you said upon which collection enumeration this error occurs, it would help). 如果您说哪个集合枚举发生此错误,它将有所帮助)。

UPDATE: PS I just thought that the error might be related to the saveMainContext function. 更新: PS我只是认为该错误可能与saveMainContext函数有关。 It is performed right before a call to saved . 它是在调用saved之前执行的。 saveMainContext is performed on the background thread (in the original code, I mean), and saved is performed on the main thread. saveMainContext在后台线程上执行(我的意思是在原始代码中),而saved在主线程上执行。 So after fixing saveMainContext , the error might go away (I'm not 100% sure, though). 因此,在修复saveMainContext ,错误可能会消失(但是我不确定100%)。

You are violating thread confinement. 您违反了线程限制。

You cannot write to CoreData in Background, and read in MainThread. 您不能在后台写入CoreData,也不能在MainThread中读取。

All operation on CoreData must be done in the same thread CoreData上的所有操作必须在同一线程中完成

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM