【问题标题】:Error on Reading from core data: ios swift从核心数据读取错误:ios swift
【发布时间】:2016-06-08 10:09:08
【问题描述】:

我正在解析来自 data source 的数据并且它们被正确解析,然后我将数据(标题、价格和日期)保存到 CoreData,标题被保存并正确读取,价格和日期被保存但在读取时他们被发现为零。 在函数读取的返回结果中,错误是“致命错误:在展开可选值时意外发现 nil”。 这是 MyTableViewController.swift :

import UIKit
import CoreData

class MyTableViewController: UITableViewController {
@IBOutlet var songsTable: UITableView!
let viewModel = ViewModel()
var imageCache = [String:UIImage]()
var songs = [NSManagedObject]()
let firstDefaults = NSUserDefaults.standardUserDefaults()
let secondDefaults = NSUserDefaults.standardUserDefaults()


override func viewDidLoad() {
    super.viewDidLoad()
    self.refresh()
    self.refreshControl = UIRefreshControl()
    self.refreshControl?.addTarget(self, action: #selector(MyTableViewController.refresh), forControlEvents: .ValueChanged)

}

func refresh() {
    viewModel.fetch {
        dispatch_async(dispatch_get_main_queue()) {
            self.tableView.reloadData()
            self.refreshControl?.endRefreshing()
        }
    }


}


override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
    return self.viewModel.numberOfSections()
}

override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return self.viewModel.numberOfItemsInSection(section)
}

override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as! MyTableViewCell
    let integer: NSInteger = indexPath.row

    if !firstDefaults.boolForKey("titlesSaved") {
        saveItem(self.viewModel.titleForItemAtIndexPath(indexPath), id: integer, name: "title")
        firstDefaults.setBool(true, forKey: "titlesSaved")
    }
    cell.songTitle.text = read(integer,item: "title") as String

    //images are not displayed
    cell.songImage?.image = UIImage(named: "Blank52.png")
    let urlString = self.viewModel.imageForItemAtIndexPath(indexPath)

    let imgURL: NSURL = NSURL(string: urlString)!
    let request: NSURLRequest = NSURLRequest(URL: imgURL)
    NSURLConnection.sendAsynchronousRequest(
        request, queue: NSOperationQueue.mainQueue(),
        completionHandler: {(response: NSURLResponse?, data: NSData?, error: NSError?) -> Void in
            if error == nil {
                cell.songImage.image = UIImage(data: data!)
            }
    })

    let thumbnailURLString = self.viewModel.imageForItemAtIndexPath(indexPath)
    let thumbnailURL = NSURL(string: thumbnailURLString)!

    //if image is cached
    if let img = imageCache[thumbnailURLString] {
        cell.songImage?.image = img
        print("image is cached")
    }
    else {
        // The image isn't cached, download the img data
        // We should perform this in a background thread
        let request: NSURLRequest = NSURLRequest(URL: thumbnailURL)
        let mainQueue = NSOperationQueue.mainQueue()
        NSURLConnection.sendAsynchronousRequest(request, queue: mainQueue, completionHandler: { (response, data, error) -> Void in
            if error == nil {
                // Convert the downloaded data in to a UIImage object
                let image = UIImage(data: data!)
                // Store the image in to our cache
                self.imageCache[thumbnailURLString] = image
                // Update the cell
                dispatch_async(dispatch_get_main_queue(), {
                    if let cellToUpdate = tableView.cellForRowAtIndexPath(indexPath) as! MyTableViewCell?{
                        cellToUpdate.songImage?.image = image
                    }
                })
            }
            else {
                print("Error: \(error!.localizedDescription)")
            }
        })
    }

    return cell
}

override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
    if let detailsViewController: DetailsViewController = segue.destinationViewController as? DetailsViewController {
        let songIndex = songsTable!.indexPathForSelectedRow
        let integer: NSInteger = songIndex!.row

        //if !secondDefaults.boolForKey("pricesAndDatesSaved") {
            saveItem(self.viewModel.priceForItemAtIndexPath(songIndex!), id: integer, name: "price")
            saveItem(self.viewModel.dateForItemAtIndexPath(songIndex!), id: integer, name: "date")
            //secondDefaults.setBool(true, forKey: "pricesAndDatesSaved")
       // }
        detailsViewController.songPrice = read(integer,item: "price") as String
        detailsViewController.songDate = read(integer,item: "date") as String
       // detailsViewController.songPrice = self.viewModel.priceForItemAtIndexPath(songIndex!)
        //detailsViewController.songDate = self.viewModel.dateForItemAtIndexPath(songIndex!)

    }
}

func saveImage(id:Int,image:UIImage)
{
    let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
    let managedContext = appDelegate.managedObjectContext

    let entity = NSEntityDescription.entityForName("Song",
                                                   inManagedObjectContext: managedContext)
    let options = NSManagedObject(entity: entity!,
                                  insertIntoManagedObjectContext:managedContext)

    let newImageData = UIImageJPEGRepresentation(image,1)

    options.setValue(id, forKey: "index")
    options.setValue(newImageData, forKey: "image")


}

func saveItem(item:String, id:Int, name: String)
{
    print("my \(name) is \(item)")
    print("my id is \(id)")
    let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
    let managedContext = appDelegate.managedObjectContext

    let entity = NSEntityDescription.entityForName("Song",
                                                   inManagedObjectContext: managedContext)
    let options = NSManagedObject(entity: entity!,
                                  insertIntoManagedObjectContext:managedContext)

    options.setValue(id, forKey: "index")
    switch name {

    case "title":
        options.setValue(item, forKey: "title")
    case "price":
        options.setValue(item, forKey: "price")
    case "date":
        options.setValue(item, forKey: "date")

    default:
        print("error in switch")
    }


    do{
        try managedContext.save()

    } catch let error as NSError{
        print("could not save title \(error)")
    }

}

func read(id: Int, item:String)-> NSString
{
    print("my id in read func is \(id)")
    let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
    let managedContext = appDelegate.managedObjectContext
    let fetchRequest = NSFetchRequest(entityName: "Song")
    var result: NSString? = nil
    do {
        let results = try managedContext.executeFetchRequest(fetchRequest)
        let single_result = results[id]

        switch item {
        case "title":
            result = single_result.valueForKey("title") as? NSString
        case "price":
            result = single_result.valueForKey("price") as? NSString
        case "date":
            result = single_result.valueForKey("date") as? NSString

        default:
            print("error in switch in read function")
        }

            print ("i am reading the \(item): \(result)")
    }catch let error as NSError{
        print("could not fetch title \(error)")
    }
    return result!
}

这是 DetailsViewController.swift

import UIKit
import CoreData

class DetailsViewController: UIViewController {

@IBOutlet weak var price: UILabel!
@IBOutlet weak var releaseDate: UILabel!
var song: ViewModel?
var songPrice: String = ""
var songDate: String = ""

override func viewDidLoad() {
    super.viewDidLoad()
    price.text = "Price = \(self.songPrice)"
    print(self.songPrice)
    print(self.songDate)
    releaseDate.text = "Release date is: \(self.songDate)"
    // Do any additional setup after loading the view.
}

override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
    // Dispose of any resources that can be recreated.
}

}

【问题讨论】:

  • 您似乎总是只保存每个核心数据条目的一个属性,而不是全部三个。这是故意的吗?这可能会导致错误。
  • 是的,我打算这样做。 @vadian
  • 那么你必须确保读取函数总是返回一个非可选值或将返回值更改为一个可选值(并使用 Swift 原生类型String 而不是NSString
  • @vadian 我正在调用 save 并在 prepareForSegue 函数中读取价格和日期
  • 在读取函数的末尾result 被解包。如果是nil,应用就会崩溃。

标签: ios swift core-data


【解决方案1】:

您正在执行以下操作:

  1. 使用标题和 ID 调用 saveItem
    1. 创建新的 NSManagedObject
    2. 在新的 NSManagedObject 上设置标题
  2. 使用价格和 ID 调用 saveItem
    1. 创建新的 NSManagedObject
    2. 为新的 NSManagedObject 设置价格
  3. 使用日期和 ID 调用 saveItem
    1. 创建新的 NSManagedObject
    2. 在新的 NSManagedObject 上设置日期

想要做的是:

  1. 使用标题和 ID 调用 saveItem
    1. 创建新的 NSManagedObject
    2. 在新的 NSManagedObject 上设置标题
  2. 使用价格和 ID 调用 updateItem
    1. 根据 ID 获取现有对象
    2. 为新的 NSManagedObject 设置价格
  3. 使用日期和 ID 调用 updateItem
    1. 根据 ID 获取现有对象
    2. 在新的 NSManagedObject 上设置命运

或者更好:

  1. 使用标题、价格、日期和 ID 调用 saveUpdateItem
    1. 如果存在则获取项目,否则创建它
    2. 更新项目

您将得到一个nil 回复,因为您正在为每个通道创建 三个 对象而不是 一个 对象。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多