ios - NSKeyedArchiver 尝试在 Swift 中持久化数据时无法保存或加载数据
问题描述
我一直在使用 Apple 的开发者教程制作一个基本的 iOS 食物追踪器。当我到达“持久化数据”步骤时,我意识到使用的功能已被弃用,所以我在网上搜索找到更新/工作版本。我找到了几个有用的 NSKeyedArchiver/Unarchiver 教程,但我的代码仍然出现错误。由于存在很多问题,我一直无法找到问题的根源(是在存档数据吗?取消存档?我使用了错误的存档 URL 吗?是在完全不同的地方出现错误吗?)。
这是我第一次在 StackOverflow 上提问,我有点急需帮助。这段代码真的让我大吃一惊,我的直觉告诉我,由于我对 Swift/Xcode 的理解有限,这可能是一个简单的问题。我已经测试了很多不同版本的 NSKeyedArchiver,我想知道它是否可能是一个设置或参数。
class MealTableViewController: UITableViewController {
//MARK: Properties
var meals = [Meal]()
override func viewDidLoad() {
super.viewDidLoad()
// Use the edit button item provided by the table view controller.
navigationItem.leftBarButtonItem = editButtonItem
// Load any saved meals, otherwise load sample data
if let savedMeals = loadMeals() {
os_log("saved meals equals load meals")
meals += savedMeals
}
else {
// Load the sample data.
loadSampleMeals()
}
}
// MARK: - Table view data source
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
return meals.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// Table view cells are reused and should be dequeued using a cell identifier
let cellIdentifier = "MealTableViewCell"
guard let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath) as? MealTableViewCell else {
fatalError("The dequeued cell is not an instance of MealTableViewCell")
}
// Fetches the appropriate meal for the data source layout.
let meal = meals[indexPath.row]
cell.nameLabel.text = meal.name
cell.photoImageView.image = meal.photo
cell.ratingControl.rating = meal.rating
return cell
}
// Override to support conditional editing of the table view.
override func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
// Return false if you do not want the specified item to be editable.
return true
}
// Override to support editing the table view.
override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete {
// Delete the row from the data source
meals.remove(at: indexPath.row)
saveMeals()
tableView.deleteRows(at: [indexPath], with: .fade)
} else if editingStyle == .insert {
// Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view
}
}
/*
// Override to support rearranging the table view.
override func tableView(_ tableView: UITableView, moveRowAt fromIndexPath: IndexPath, to: IndexPath) {
}
*/
/*
// Override to support conditional rearranging of the table view.
override func tableView(_ tableView: UITableView, canMoveRowAt indexPath: IndexPath) -> Bool {
// Return false if you do not want the item to be re-orderable.
return true
}
*/
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
super.prepare(for: segue, sender: sender)
switch(segue.identifier ?? ""){
case "AddItem":
os_log("Adding a new meal.", log: OSLog.default, type: .debug)
case "ShowDetail":
guard let mealDetailViewController = segue.destination as? MealViewController else{
fatalError("Unexpected destination: \(segue.destination)")
}
guard let selectedMealCell = sender as? MealTableViewCell else{
fatalError("Unexpected sender: \(String(describing: sender))")
}
guard let indexPath = tableView.indexPath(for: selectedMealCell) else{
fatalError("The selected cell is not being displayed by the table")
}
let selectedMeal = meals[indexPath.row]
mealDetailViewController.meal = selectedMeal
default:
fatalError("Unexpected Segue Identifier; \(String(describing: segue.identifier))")
}
}
//MARK: Private Methods
private func loadSampleMeals(){
let burger = UIImage(named: "burger")
let sandwich = UIImage(named: "sandwich")
let coffee = UIImage(named: "coffee")
let pizza = UIImage(named: "pizza")
guard let meal1 = Meal(name: "Burger", photo: burger, rating: 4) else{
fatalError("Unable to instantiate burger")
}
guard let meal2 = Meal(name: "Sandwich", photo: sandwich, rating: 2) else{
fatalError("Unable to instantiate sandwich")
}
guard let meal3 = Meal(name: "Coffee", photo: coffee, rating: 5) else{
fatalError("Unable to instantiate coffee")
}
guard let meal4 = Meal(name: "Pizza", photo: pizza, rating: 4) else{
fatalError("Unable to instantiate pizza")
}
meals += [meal1, meal2, meal3, meal4]
}
private func saveMeals() {
do {
let mealData = try NSKeyedArchiver.archivedData(withRootObject: meals, requiringSecureCoding: true)
try mealData.write(to: Meal.ArchiveURL)
print(mealData)
os_log("Meals successfully saved.", log: OSLog.default, type: .debug)
} catch {
os_log("Failed to save meals...", log: OSLog.default, type: .error)
}
}
//MARK: Actions
@IBAction func unwindToMealList(sender: UIStoryboardSegue) {
if let sourceViewController = sender.source as? MealViewController, let meal = sourceViewController.meal {
if let selectedIndexPath = tableView.indexPathForSelectedRow{
// Update an existing meal.
meals[selectedIndexPath.row] = meal
tableView.reloadRows(at: [selectedIndexPath], with: .none)
}
else {
// Add a new meal.
let newIndexPath = IndexPath(row: meals.count, section: 0)
meals.append(meal)
tableView.insertRows(at: [newIndexPath], with: .automatic)
}
// Save the meals.
saveMeals()
}
}
private func loadMeals() -> [Meal]? {
do {
let fileData = try Data(contentsOf: Meal.ArchiveURL)
let loadedStrings = try NSKeyedUnarchiver.unarchivedObject(ofClass: Meal.self, from: fileData)
print("unarchived Strings worked")
} catch {
// print("Couldn't read file. \(error)")
print("Couldn't find file.")
print(Meal.ArchiveURL)
}
return meals
}
}
我一直在单步执行代码,并注意到根本问题可能在这里:
private func saveMeals() {
do {
let mealData = try NSKeyedArchiver.archivedData(withRootObject: meals, requiringSecureCoding: true)
try mealData.write(to: Meal.ArchiveURL)
print(mealData)
os_log("Meals successfully saved.", log: OSLog.default, type: .debug)
} catch {
os_log("Failed to save meals...", log: OSLog.default, type: .error)
}
}
当它到达“尝试 NSKeyedArchiver.archivedData(withRootObject:饭菜, requiresSecureCoding: true)”时,它立即进入“catch”,我不知道为什么。
解决方案
推荐阅读
- mysql - 如何从比较主查询的值的子查询中获取具体行?
- promise - 避免嵌套 promises.eslint(promise/no-nesting)
- ruby-on-rails - Redis 上的连接数会限制我可以在后台执行的作业数吗?
- python - Python 字符串到浮点数的 8 位小数转换
- google-apps-script - 使用 Apps 脚本将谷歌文档转换为 PDF
- excel - 使用vba的.ExportAsFixedFormat方法从excel打印没有边距的图表?
- html - 在哪里可以删除自定义 HTML 属性?
- redux - 我怎样才能让 (i18next) 与 redux 一起工作?
- docker - 雷达+传输+码头;远程路径出错
- tensorflow - 如何为自定义 keras 层创建可训练的权重变量