iOS 8核心数据堆栈 – 致命错误:发现零,同时展开一个可选值

我对iOS开发比较陌生,决定实现自己的Core Data堆栈,取代Apple的默认堆栈。

我不得不在我的代码中进行更改(很明显),并能够弄清楚,但在这种情况下我不能。 这是我的代码:

import UIKit import CoreData class AddTableViewController: UITableViewController, UIImagePickerControllerDelegate, UINavigationControllerDelegate { @IBOutlet weak var nameTextField:UITextField! @IBOutlet weak var locationTextField:UITextField! @IBOutlet weak var imageView:UIImageView! @IBOutlet weak var notesView:UITextView! var coreDataStack = (UIApplication.sharedApplication().delegate as AppDelegate).coreDataStack var backpackerSpot:BackpackerSpot! var managedContext: NSManagedObjectContext! override func viewDidLoad() { super.viewDidLoad() } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() // Dispose of any resources that can be recreated. } // TODO Give user the choice of the Photo Library or the Camera override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) { if indexPath.row == 0 { if UIImagePickerController.isSourceTypeAvailable(.Camera) { let imagePicker = UIImagePickerController() imagePicker.allowsEditing = false imagePicker.delegate = self imagePicker.sourceType = .Camera self.presentViewController(imagePicker, animated: true, completion: nil) } } tableView.deselectRowAtIndexPath(indexPath, animated: true) } // FIXME image is being displayed in landscape if it is taken in portrait mode by default func imagePickerController(picker: UIImagePickerController!, didFinishPickingImage image: UIImage!, editingInfo: [NSObject : AnyObject]!) { imageView.image = image imageView.contentMode = UIViewContentMode.ScaleAspectFill imageView.clipsToBounds = true dismissViewControllerAnimated(true, completion: nil) } @IBAction func save() { //validation var errorField = "" // TODO have placeholder text in the NOTES field match up with the placholder text in the NAME and LOCATION fields. if nameTextField.text == "" { errorField = "name" } else if locationTextField.text == "" { errorField = "location" } else if notesView.text == "" { errorField = "notes" } if errorField != "" { let alertController = UIAlertController(title: "Error", message: "You must fill in \(errorField).", preferredStyle: .Alert) let doneAction = UIAlertAction(title: "OK", style: .Default, handler: nil) alertController.addAction(doneAction) self.presentViewController(alertController, animated: true, completion: nil) return } // If all fields are correctly filled in, extract the field value // Create Restaurant Object and save to data store // if let managedObjectContext = (UIApplication.sharedApplication().delegate as AppDelegate).coreDataStack.context { let entityBackpackerSpot = NSEntityDescription.entityForName("BackpackerSpot", inManagedObjectContext: coreDataStack.context) backpackerSpot?.spotName = nameTextField.text backpackerSpot?.spotLocation = locationTextField.text backpackerSpot?.spotImage = UIImagePNGRepresentation(imageView.image) backpackerSpot?.spotNote = notesView.text var error: NSError? if !managedContext.save(&error) { println("insert error: \(error!.localizedDescription)") return } // Execute the unwind segue and go back to the home screen performSegueWithIdentifier("unwindToHomeScreen", sender: self) } } 

该应用程序启动正常,但是当我点击附加到保存function的UIButton,它崩溃,我得到以下错误:

 fatal error: unexpectedly found nil while unwrapping an Optional value (lldb) 

debugging器突出显示的行是:

  if !managedContext.save(&error) { 

无可否认,我必须花更多的时间在Swift中使用Optionals,因为它们似乎经常成为我的麻烦来源,尽pipe通常我能够弄明白。 如果有人能指出我的方向是正确的,那就太好了。 谢谢。

编辑:这是我的核心数据堆栈:

 import Foundation import CoreData class CoreDataStack { let model: NSManagedObjectModel let storeCoordinator: NSPersistentStoreCoordinator let context: NSManagedObjectContext let store: NSPersistentStore? let dbName = "BackpackerSpots" // these options do the migration for us let options = [NSInferMappingModelAutomaticallyOption:true, NSMigratePersistentStoresAutomaticallyOption: true] init() { //1 loading model from the file let bundle = NSBundle.mainBundle() let modelURL = bundle.URLForResource(dbName, withExtension: "momd") model = NSManagedObjectModel(contentsOfURL: modelURL!)! //2 store coordinator created using that model storeCoordinator = NSPersistentStoreCoordinator(managedObjectModel: model) //3 context context = NSManagedObjectContext() context.persistentStoreCoordinator = storeCoordinator //4. store let documentsURL = MyDocumentDirectory() let storeURL = documentsURL.URLByAppendingPathComponent(dbName) var error:NSError? store = storeCoordinator.addPersistentStoreWithType(NSSQLiteStoreType, configuration: nil, URL: storeURL, options: nil, error: &error) if store == nil { println("error adding persistent store: \(error)") abort() } } func saveContext() { var error: NSError? if context.hasChanges && !context.save(&error) { println("Could not save. \(error), \(error?.description)") } } } 

编辑:这是我的根视图控制器:

 import UIKit import CoreData class BackpackerSpotTableViewController: UITableViewController, NSFetchedResultsControllerDelegate { var coreDataStack = (UIApplication.sharedApplication().delegate as AppDelegate).coreDataStack // var backpackerSpot:BackpackerSpot! var managedContext: NSManagedObjectContext! var backpackerSpots:[BackpackerSpot] = [] var fetchResultController:NSFetchedResultsController! // Let's add some animations! override func tableView(tableView: UITableView, willDisplayCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) { // initial cell state cell.alpha = 0 // state after animations UIView.animateWithDuration(1.0, animations: { cell.alpha = 1}) } override func viewDidLoad() { super.viewDidLoad() var fetchRequest = NSFetchRequest(entityName: "BackpackerSpot") let sortDescriptor = NSSortDescriptor(key: "spotName", ascending: true) fetchRequest.sortDescriptors = [sortDescriptor] // if let managedObjectContext = (UIApplication.sharedApplication().delegate as AppDelegate).coreDataStack { let entityBackpackerSpot = NSEntityDescription.entityForName("BackpackerSpot", inManagedObjectContext: coreDataStack.context) fetchResultController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: coreDataStack.context, sectionNameKeyPath: nil, cacheName: nil) fetchResultController.delegate = self var error: NSError? var result = fetchResultController.performFetch(&error) backpackerSpots = fetchResultController.fetchedObjects as [BackpackerSpot] if result != true { println(error?.localizedDescription) } } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() // Dispose of any resources that can be recreated. } // MARK: - Table view data source override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return self.backpackerSpots.count } override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { let cellIdentifier = "Cell" let cell = tableView.dequeueReusableCellWithIdentifier(cellIdentifier, forIndexPath: indexPath) as CustomTableViewCell let backpackerSpot = backpackerSpots[indexPath.row] cell.nameLabel.text = backpackerSpot.spotName cell.thumbnailImageView.image = UIImage(data: backpackerSpot.spotImage) cell.locationLabel.text = backpackerSpot.spotLocation return cell } override func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) { } override func tableView(tableView: UITableView, editActionsForRowAtIndexPath indexPath: NSIndexPath) -> [AnyObject]? { var deleteAction = UITableViewRowAction(style: UITableViewRowActionStyle.Default, title: "Delete",handler: { (action:UITableViewRowAction!, indexPath:NSIndexPath!) -> Void in // if let managedObjectContext = (UIApplication.sharedApplication().delegate as AppDelegate).managedObjectContext { let entityBackpackerSpot = NSEntityDescription.entityForName("BackpackerSpot", inManagedObjectContext: self.coreDataStack.context) let backpackerSpotToDelete = self.fetchResultController.objectAtIndexPath(indexPath) as BackpackerSpot self.managedContext.deleteObject(backpackerSpotToDelete) var error: NSError? if !self.managedContext.save(&error) { println("Could not save \(error), \(error?.description)") } }) deleteAction.backgroundColor = UIColor(red: 169.0/255.0, green: 37.0/255.0, blue: 50.0/255.0, alpha: 1.0) return [deleteAction] } func controllerWillChangeContent(controller: NSFetchedResultsController!) { tableView.beginUpdates() } 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: tableView.reloadRowsAtIndexPaths([indexPath], withRowAnimation: .Fade) default: tableView.reloadData() } backpackerSpots = controller.fetchedObjects as [BackpackerSpot] } func controllerDidChangeContent(controller: NSFetchedResultsController!) { tableView.endUpdates() } override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) { if segue.identifier == "showBackpackerSpotDetails" { if let row = tableView.indexPathForSelectedRow()?.row { let destinationController = segue.destinationViewController as DetailViewController destinationController.backpackerSpot = backpackerSpots[row] } } } @IBAction func unwindToHomeScreen(segue: UIStoryboardSegue) { } } 

你还没有初始化你的NSManagedContextvariables。 使用默认的核心数据设置你可以这样做:

  override func viewDidLoad() { if let appDelegate = UIApplication.sharedApplication().delegate as? AppDelegate { managedContext = appDelegate.managedObjectContext } } 

编辑:鉴于你的核心数据设置,你应该这样做:

  override func viewDidLoad() { managedContext = coreDataStack.context }