在核心数据更改处理期间捕获到异常

Posted

技术标签:

【中文标题】在核心数据更改处理期间捕获到异常【英文标题】:Exception was caught during Core Data change processing 【发布时间】:2015-07-19 16:00:02 【问题描述】:

在我的应用程序中,我在AppDelegate 中创建了我的CoreDataStack,并将该实例传递给我的根视图控制器。在视图控制器中,我连接到一个 API,检索和解析一些 JSON,然后保存到 Core Data。但是,对于大约十分之一的发布,我会立即发生崩溃,这发生在我的 CoreDataStack 类中:

func saveContext () 
    if let moc = self.managedObjectContext 
        var error: NSError? = nil

        // This if-statement is the line that gets highlighted following the crash.
        if moc.hasChanges && !moc.save(&error) 
            // Replace this implementation with code to handle the error appropriately.
            // abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
            NSLog("Unresolved error \(error), \(error!.userInfo)")
            abort()
        
    

我得到的日志是:

CoreData: error: Serious application error.  
Exception was caught during Core Data change processing.  This is usually a bug within an observer of NSManagedObjectContextObjectsDidChangeNotification.  
-[__NSCFSet addObject:]: attempt to insert nil with userInfo (null)
2015-07-19 11:33:06.115 AppName[210:3907] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSCFSet addObject:]: attempt to insert nil'

在我的视图控制器中,我进入主线程以保存到核心数据。崩溃后,在 Debug Navigator 中浏览堆栈时,self.coreDataStack!.saveContext() 会突出显示。我假设我试图在不同的线程上创建和访问managedObjectContext,但似乎并非如此,因为当我调用println(NSThread.currentThread() 时,我得到的线程号和名称与打印时相同来自AppDelegate,我在这里创建了CoreDataStack 实例。

func updateStockInformation(#completion: ((finished: Bool) -> Void)?) 
    UIApplication.sharedApplication().networkActivityIndicatorVisible = true
    if stocks.count > 0 
        var numberOfStocksUpdated = 0
        for stock in stocks 
            yql.query(stock.symbol, completion:  (results) -> Void in
                if results != nil 
                    if let query = results!["query"] as? NSDictionary, results = query["results"] as? NSDictionary, quote = results["quote"] as? NSDictionary, lastTradePrice = quote["LastTradePriceOnly"] as? NSString 
                        if let change = quote["Change"] as? NSString 
                            var changeValue = NSDecimalNumber()
                            if (change as String).hasPrefix("-") 
                                let unsignedNegativeString = change.stringByReplacingOccurrencesOfString("-", withString: "")
                                let unsignedNegativeNumber = NSDecimalNumber(string: unsignedNegativeString)
                                let signedNegativeNumber = unsignedNegativeNumber.negative()
                                changeValue = signedNegativeNumber
                             else 
                                let unsignedPositiveString = change.stringByReplacingOccurrencesOfString("+", withString: "")
                                let unsignedPositiveNumber = NSDecimalNumber(string: unsignedPositiveString)
                                changeValue = unsignedPositiveNumber
                            
                            if self.market.status == .Open 
                                stock.change = changeValue
                            
                        
                        var priceValue = NSDecimalNumber()
                        // Change the 'lastTradePrice' value from an NSString into an NSDecimalNumber
                        priceValue = NSDecimalNumber(string: lastTradePrice as String)
                        stock.lastTradePrice = priceValue
                        stock.profitFromStocksInCompany = self.updateProfitForStock(stock)
                        numberOfStocksUpdated += 1
                    
                
                if numberOfStocksUpdated == self.stocks.count 
                    dispatch_async(dispatch_get_main_queue(),  () -> Void in
                        println("MainViewController - updateStockInformation: \(NSThread.currentThread())")
                        println()
                        self.coreDataStack!.saveContext()
                        self.updateTotalProfitLabel()
                        self.reloadPageViewController()
                        UIApplication.sharedApplication().networkActivityIndicatorVisible = false 
                        self.stockInformationUpdatedDelegate?.stockInformationDidUpdate(self.market.getStatusOfMarket())
                        completion?(finished: true)
                    )
                
            )
        
    

关于我做错了什么导致偶尔崩溃的任何想法?

【问题讨论】:

【参考方案1】:

当我打印出 JSON 数据时,我注意到所有返回的东西都是混乱的,这意味着在接收和解析前一个网络请求的数据之前,for 循环正在快速发送网络请求。所以我决定重组我的代码并使用递归。下面是新改组的代码,它使用递归来更新一只股票的信息,只有在前一只股票的信息被接收、解析和更新后:

func updateStockInformation(#completion: ((finished: Bool) -> Void)?) 
    updateStock(0, completion: nil)
    if stocksUpdatingAppBecameActive 
        stocksUpdatingAppBecameActive = false
    




func updateStock(stockIndex: Int, completion: ((finished: Bool) -> Void)?) 
    var stockIndex = stockIndex
    UIApplication.sharedApplication().networkActivityIndicatorVisible = true
    if stocks.count > 0 
        let stock = stocks[stockIndex]
        yql.query(stock.symbol, completion:  (results) -> Void in
            if results != nil 
                if let query = results!["query"] as? NSDictionary, results = query["results"] as? NSDictionary, quote = results["quote"] as? NSDictionary, lastTradePrice = quote["LastTradePriceOnly"] as? NSString 
                    if let change = quote["Change"] as? NSString 
                        var changeValue = NSDecimalNumber()
                        // Change the 'change' value from an NSString into an NSDecimalNumber
                        if (change as String).hasPrefix("-") 
                            let unsignedNegativeString = change.stringByReplacingOccurrencesOfString("-", withString: "")
                            let unsignedNegativeNumber = NSDecimalNumber(string: unsignedNegativeString)
                            let signedNegativeNumber = unsignedNegativeNumber.negative()
                            changeValue = signedNegativeNumber
                         else 
                            let unsignedPositiveString = change.stringByReplacingOccurrencesOfString("+", withString: "")
                            let unsignedPositiveNumber = NSDecimalNumber(string: unsignedPositiveString)
                            changeValue = unsignedPositiveNumber
                        
                        if self.market.status == .Open 
                            stock.change = changeValue
                        
                    
                    var priceValue = NSDecimalNumber()
                    priceValue = NSDecimalNumber(string: lastTradePrice as String)
                    stock.lastTradePrice = priceValue
                    stock.profitFromStocksInCompany = self.updateProfitForStock(stock)
                    self.numberOfStocksUpdated += 1
                
            
            if self.numberOfStocksUpdated == self.stocks.count 
                dispatch_async(dispatch_get_main_queue(),  () -> Void in
                    self.coreDataStack!.saveContext()
                    self.updateTotalProfitLabel()
                    self.reloadPageViewController()
                    UIApplication.sharedApplication().networkActivityIndicatorVisible = false
                    self.stockInformationUpdatedDelegate?.stockInformationDidUpdate(self.market.getStatusOfMarket())
                    self.numberOfStocksUpdated = 0
                    completion?(finished: true)
                )
             else 
                self.updateStock(stockIndex + 1, completion: nil)
            
        )
    

【讨论】:

以上是关于在核心数据更改处理期间捕获到异常的主要内容,如果未能解决你的问题,请参考以下文章

CoreData:错误:严重的应用程序错误。在核心数据更改处理期间捕获到异常

Swift CoreData:错误:严重的应用程序错误。在核心数据更改处理期间捕获到异常。

无法保存核心数据

合并 NSManagedObjectContexts 之间的更改(多线程)

PHP 核心特性 - 错误处理

带有核心数据未捕获的 NSExeption 类型异常的 SIGABRT