Book (Swift for Dummies) 第 4 章练习:致命错误:在展开 Optional 值时意外发现 nil
Posted
技术标签:
【中文标题】Book (Swift for Dummies) 第 4 章练习:致命错误:在展开 Optional 值时意外发现 nil【英文标题】:Book (Swift for Dummies) Chapter 4 Exercise: fatal error: unexpectedly found nil while unwrapping an Optional value 【发布时间】:2015-07-16 05:27:02 【问题描述】:我正在学习swift,正在阅读《Swift for Dummies》一书,我试图按照作者的做法做书中的例子,但是我在第四章的例子中遇到了问题,你能各位帮帮我。
第 51 行的错误
MasterViewController.swift
newManagedObject.latitude = self.lastLocation.coordinate.latitude
线程 1:EXC_BAD_INSTRUCTION(代码=EXC_I386_INVOP,子代码=0x0)在 输出窗口显示致命错误:意外发现 nil 而 展开可选值 (lldb)
import UIKit
import CoreData
import CoreLocation
class MasterViewController: UITableViewController,
NSFetchedResultsControllerDelegate,
CLLocationManagerDelegate
var detailViewController: DetailViewController? = nil
var managedObjectContext: NSManagedObjectContext? = nil
let locationManager = CLLocationManager()
var lastLocation: CLLocation! = nil
override func awakeFromNib()
super.awakeFromNib()
override func viewDidLoad()
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
self.navigationItem.leftBarButtonItem = self.editButtonItem()
let addButton = UIBarButtonItem(barButtonSystemItem: .Add, target: self, action: "insertNewObject:")
self.navigationItem.rightBarButtonItem = addButton
self.startSignificantChangeUpdates()
override func didReceiveMemoryWarning()
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
func insertNewObject(sender: AnyObject)
let context = self.fetchedResultsController.managedObjectContext
let entity = self.fetchedResultsController.fetchRequest.entity!
let newManagedObject = NSEntityDescription.insertNewObjectForEntityForName("Event", inManagedObjectContext: context) as! Event
// If appropriate, configure the new managed object.
// Normally you should use accessor methods, but using KVC here avoids the need to add a custom class to the template.
newManagedObject.latitude = self.lastLocation.coordinate.latitude
newManagedObject.longitude = self.lastLocation.coordinate.longitude
// Save the context.
var error: NSError? = nil
if !context.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.
//println("Unresolved error \(error), \(error.userInfo)")
abort()
// MARK: - Segues
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?)
if segue.identifier == "showDetail"
if let indexPath = self.tableView.indexPathForSelectedRow()
let object = self.fetchedResultsController.objectAtIndexPath(indexPath) as! NSManagedObject
(segue.destinationViewController as! DetailViewController).detailItem = object
// MARK: - Table View
override func numberOfSectionsInTableView(tableView: UITableView) -> Int
return self.fetchedResultsController.sections?.count ?? 0
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int
let sectionInfo = self.fetchedResultsController.sections![section] as! NSFetchedResultsSectionInfo
return sectionInfo.numberOfObjects
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as! UITableViewCell
self.configureCell(cell, atIndexPath: indexPath)
return cell
override func tableView(tableView: UITableView, canEditRowAtIndexPath indexPath: NSIndexPath) -> Bool
// Return false if you do not want the specified item to be editable.
return true
override func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath)
if editingStyle == .Delete
let context = self.fetchedResultsController.managedObjectContext
context.deleteObject(self.fetchedResultsController.objectAtIndexPath(indexPath) as! NSManagedObject)
var error: NSError? = nil
if !context.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.
//println("Unresolved error \(error), \(error.userInfo)")
abort()
func configureCell(cell: UITableViewCell, atIndexPath indexPath: NSIndexPath)
let object = self.fetchedResultsController.objectAtIndexPath(indexPath) as! Event
cell.textLabel!.text = "latitude: " + object.latitude.description + " longitude: " + object.longitude.description
// MARK: - Fetched results controller
var fetchedResultsController: NSFetchedResultsController
if _fetchedResultsController != nil
return _fetchedResultsController!
let fetchRequest = NSFetchRequest()
// Edit the entity name as appropriate.
let entity = NSEntityDescription.entityForName("Event", inManagedObjectContext: self.managedObjectContext!)
fetchRequest.entity = entity
// Set the batch size to a suitable number.
fetchRequest.fetchBatchSize = 20
// Edit the sort key as appropriate.
let sortDescriptor = NSSortDescriptor(key: "timeStamp", ascending: false)
let sortDescriptors = [sortDescriptor]
fetchRequest.sortDescriptors = [sortDescriptor]
// Edit the section name key path and cache name if appropriate.
// nil for section name key path means "no sections".
let aFetchedResultsController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: self.managedObjectContext!, sectionNameKeyPath: nil, cacheName: "Master")
aFetchedResultsController.delegate = self
_fetchedResultsController = aFetchedResultsController
var error: NSError? = nil
if !_fetchedResultsController!.performFetch(&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.
//println("Unresolved error \(error), \(error.userInfo)")
abort()
return _fetchedResultsController!
var _fetchedResultsController: NSFetchedResultsController? = nil
func controllerWillChangeContent(controller: NSFetchedResultsController)
self.tableView.beginUpdates()
func controller(controller: NSFetchedResultsController, didChangeSection sectionInfo: NSFetchedResultsSectionInfo, atIndex sectionIndex: Int, forChangeType type: NSFetchedResultsChangeType)
switch type
case .Insert:
self.tableView.insertSections(NSIndexSet(index: sectionIndex), withRowAnimation: .Fade)
case .Delete:
self.tableView.deleteSections(NSIndexSet(index: sectionIndex), withRowAnimation: .Fade)
default:
return
func controller(controller: NSFetchedResultsController, didChangeObject anObject: AnyObject, atIndexPath indexPath: NSIndexPath?, forChangeType type: NSFetchedResultsChangeType, newIndexPath: NSIndexPath?)
switch type
case .Insert:
tableView.insertRowsAtIndexPaths([newIndexPath!], withRowAnimation: .Fade)
case .Delete:
tableView.deleteRowsAtIndexPaths([indexPath!], withRowAnimation: .Fade)
case .Update:
self.configureCell(tableView.cellForRowAtIndexPath(indexPath!)!, atIndexPath: indexPath!)
case .Move:
tableView.deleteRowsAtIndexPaths([indexPath!], withRowAnimation: .Fade)
tableView.insertRowsAtIndexPaths([newIndexPath!], withRowAnimation: .Fade)
default:
return
func controllerDidChangeContent(controller: NSFetchedResultsController)
self.tableView.endUpdates()
/*
// Implementing the above methods to update the table view in response to individual changes may have performance implications if a large number of changes are made simultaneously. If this proves to be an issue, you can instead just implement controllerDidChangeContent: which notifies the delegate that all section and object changes have been processed.
func controllerDidChangeContent(controller: NSFetchedResultsController)
// In the simplest, most efficient, case, reload the table view.
self.tableView.reloadData()
*/
// MARK: CLLocationManagerDelegate Protocol
func startSignificantChangeUpdates ()
if CLLocationManager.authorizationStatus() == CLAuthorizationStatus.NotDetermined
self.locationManager.requestWhenInUseAuthorization()
if CLLocationManager.locationServicesEnabled()
self.locationManager.delegate = self
self.locationManager.distanceFilter = kCLDistanceFilterNone
self.locationManager.desiredAccuracy = kCLLocationAccuracyBest
self.locationManager.startUpdatingLocation()
self.locationManager.startMonitoringSignificantLocationChanges()
func locationManager (manager: CLLocationManager!, didUpdateLocations locations:[AnyObject]!)
self.lastLocation = manager.location
let eventDate = self.lastLocation.timestamp;
let howRecent = eventDate.timeIntervalSinceNow;
func locationManager(manager: CLLocationManager!, didFailWithError error: NSError!)
// need to add code to catch errors
import UIKit
import MapKit
class DetailViewController: UIViewController
@IBOutlet var mapView: MKMapView!
@IBOutlet weak var detailDescriptionLabel: UILabel!
var detailItem: AnyObject?
didSet
// Update the view.
self.configureView()
func configureView()
// Update the user interface for the detail item.
if let detail: AnyObject = self.detailItem
if let label = self.detailDescriptionLabel
label.text = detail.valueForKey("timeStamp")!.description
override func viewDidLoad()
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
self.configureView()
override func didReceiveMemoryWarning()
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
import Foundation
import CoreData
class Event: NSManagedObject
@NSManaged var latitude: NSNumber
@NSManaged var longitude: NSNumber
@NSManaged var timeStamp: NSDate
import UIKit
import CoreData
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate
var window: UIWindow?
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool
// Override point for customization after application launch.
let navigationController = self.window!.rootViewController as! UINavigationController
let controller = navigationController.topViewController as! MasterViewController
controller.managedObjectContext = self.managedObjectContext
return true
func applicationWillResignActive(application: UIApplication)
// Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
// Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
func applicationDidEnterBackground(application: UIApplication)
// Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
// If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
func applicationWillEnterForeground(application: UIApplication)
// Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
func applicationDidBecomeActive(application: UIApplication)
// Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
func applicationWillTerminate(application: UIApplication)
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
// Saves changes in the application's managed object context before the application terminates.
self.saveContext()
// MARK: - Core Data stack
lazy var applicationDocumentsDirectory: NSURL =
// The directory the application uses to store the Core Data store file. This code uses a directory named "com.sauer.Locatapp" in the application's documents Application Support directory.
let urls = NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask)
return urls[urls.count-1] as! NSURL
()
lazy var managedObjectModel: NSManagedObjectModel =
// The managed object model for the application. This property is not optional. It is a fatal error for the application not to be able to find and load its model.
let modelURL = NSBundle.mainBundle().URLForResource("Locatapp", withExtension: "momd")!
return NSManagedObjectModel(contentsOfURL: modelURL)!
()
lazy var persistentStoreCoordinator: NSPersistentStoreCoordinator? =
// The persistent store coordinator for the application. This implementation creates and return a coordinator, having added the store for the application to it. This property is optional since there are legitimate error conditions that could cause the creation of the store to fail.
// Create the coordinator and store
var coordinator: NSPersistentStoreCoordinator? = NSPersistentStoreCoordinator(managedObjectModel: self.managedObjectModel)
let url = self.applicationDocumentsDirectory.URLByAppendingPathComponent("Locatapp.sqlite")
var error: NSError? = nil
var failureReason = "There was an error creating or loading the application's saved data."
if coordinator!.addPersistentStoreWithType(NSSQLiteStoreType, configuration: nil, URL: url, options: nil, error: &error) == nil
coordinator = nil
// Report any error we got.
var dict = [String: AnyObject]()
dict[NSLocalizedDescriptionKey] = "Failed to initialize the application's saved data"
dict[NSLocalizedFailureReasonErrorKey] = failureReason
dict[NSUnderlyingErrorKey] = error
error = NSError(domain: "YOUR_ERROR_DOMAIN", code: 9999, userInfo: dict)
// Replace this 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()
return coordinator
()
lazy var managedObjectContext: NSManagedObjectContext? =
// Returns the managed object context for the application (which is already bound to the persistent store coordinator for the application.) This property is optional since there are legitimate error conditions that could cause the creation of the context to fail.
let coordinator = self.persistentStoreCoordinator
if coordinator == nil
return nil
var managedObjectContext = NSManagedObjectContext()
managedObjectContext.persistentStoreCoordinator = coordinator
return managedObjectContext
()
// MARK: - Core Data Saving support
func saveContext ()
if let moc = self.managedObjectContext
var error: NSError? = nil
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()
【问题讨论】:
因为self.lastLocation.coordinate.latitude
中没有值。请检查您是否获得任何位置。
【参考方案1】:
此变量为空
self.lastLocation
你应该调用这个函数
func locationManager (manager: CLLocationManager!, didUpdateLocations locations:[AnyObject]!)
在某处初始化 lastLocation。所以它不会为空
或者你可以改变
var lastLocation: CLLocation! = nil
到这里
var lastLocation: CLLocation? = nil
所以当运行这段代码时
newManagedObject.latitude = self.lastLocation?.coordinate.latitude
它将跳过这一行,因为 self.lastLocation 为 null 而不是显示 EXC_BAD_INSTRUCTION 错误
【讨论】:
应用程序启动,但我没有得到位置...当我点击 + 按钮时,他关闭并给出此错误...我按照你告诉我的做了...但我仍然得到一个问题...现在换一个...我将这一行更改为 var lastLocation: CLLocation? = nil 而这个是 newManagedObject.latitude = self.lastLocation?.coordinate.latitude 我得到了这个错误:可选类型'CLLocationDegrees?'的值没有打开你的意思是使用'!'或者 '?'?修改后...应用程序不再启动。 在调用这个newManagedObject.latitude = self.lastLocation.coordinate.latitude之前你有没有调用locationManager的构造函数? 我正在学习 Swift... 怎么调用构造函数???也许我打过电话,但我不确定,因为我不知道该怎么做……以上是关于Book (Swift for Dummies) 第 4 章练习:致命错误:在展开 Optional 值时意外发现 nil的主要内容,如果未能解决你的问题,请参考以下文章
什么是循环冗余检查以及它是如何简单地工作的(for-dummies 风格)?
如何在 pyqt 中嵌入 matplotlib - For Dummies