[英]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: 错误:
'NSGenericException', reason: Collection <__NSCFSet: 0x16e47100> was mutated while being enumerated
'NSGenericException',原因:枚举时集合<__ NSCFSet:0x16e47100>发生了变异
Some times : crash here
try managedObjectContext.save()
有时:在此处崩溃请
try managedObjectContext.save()
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.