[英]CoreData Concurrency issue
使用private managedObjectContext
在后台保存数据时出现问题。 我是CoreData的新手。 我正在为NSManagedObjectContext
使用Parent-Child方法,但是遇到了几个问题。
当我多次点击重新加载按钮时出现错误
错误:
'NSGenericException',原因:枚举时集合<__ NSCFSet:0x16e47100>发生了变异
有时:在此处崩溃请
try managedObjectContext.save()
有时键值编码符合错误
我的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.