在Core Data更改处理期间捕获到异常

时间:2015-07-19 16:00:02

标签: core-data

在我的应用程序中,我在CoreDataStack中创建AppDelegate并将该实例传递给我的根视图控制器。在视图控制器中,我连接到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'

在我的视图控制器中,我进入主线程以保存到Core Data。崩溃后,在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 个答案:

答案 0 :(得分:0)

当我打印出JSON数据时,我注意到所有内容都是混乱的,这意味着for-loop在接收和解析之前的网络请求数据之前快速发送网络请求。所以我决定重构我的代码并使用递归。以下是新重组的代码,仅在收到,解析和更新上一个库存的信息后才使用递归来更新库存信息:

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)
            }
        })
    }
}