我一直在使用Apple的Developer教程来开发基本的iOS Food Tracker。当我进入“持久数据”步骤时,我意识到所使用的功能已被弃用,因此我在网上搜索以找到更新/工作版本。我找到了一些有用的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:餐,需要SecureeCoding:true)”时,它立即转到“捕获”位置,我不确定为什么。