在我的应用程序中,我在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)
})
}
})
}
}
}
关于我正在做什么的任何想法都不正确地导致这种偶然的崩溃?
答案 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)
}
})
}
}