[英]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.