Завершающее приложение из-за неперехваченного исключения ‘NSInvalidArgumentException’, причина: ‘ [NSKeyedArchiver unarchiveObjectWithFile:]

#swift #unrecognized-selector #nskeyedarchiver

#swift #нераспознанный селектор #nskeyedarchiver

Вопрос:

Я слежу за демонстрацией FoodTracker у разработчика Apple. Я перехожу к последнему разделу: сохранение данных.

Я запускаю Xcode 8 и слежу за демонстрационной сборкой на Swift 3.

Если я загружу код и конвертирую в Swift 2.3, это не сработает. Если я перейду на Swift 3.0, это тоже не сработает. Я пытался следить за демонстрацией на Swift 3.

Код требует некоторых изменений для компиляции и запуска на Swift 3, и до сих пор я добивался успеха.

Однако теперь я получаю это во время выполнения:

FoodTracker [1597:720222] *** Завершающее приложение из-за неперехваченного исключения ‘NSInvalidArgumentException’, причина: ‘ [NSKeyedArchiver unarchiveObjectWithFile:]: нераспознанный селектор, отправленный в класс 0x1081497a8’

Изменения, которые я должен был внести в этот раздел, заключались в следующем: 1) В классе Meal я добавил две статические константы:

 static let DocumentsDirectory =
   FileManager().urls(for: .documentDirectory, in:
    .userDomainMask).first! 
static let ArchiveURL = DocumentsDirectory.appendingPathComponent("meals")
  

2) Также в классе Meal я добавил struct и удобный init:

 func encode(with aCoder: NSCoder) {
    aCoder.encode(name, forKey: PropertyKey.nameKey)
    aCoder.encode(photo, forKey: PropertyKey.photoKey)
    aCoder.encode(rating, forKey: PropertyKey.ratingKey)
}

required convenience init?(coder aDecoder: NSCoder) {
    let name = aDecoder.decodeObject(forKey: PropertyKey.nameKey) as! String

    // Because photo is an optional property of Meal, use conditional cast
    let photo = aDecoder.decodeObject(forKey: PropertyKey.photoKey) as? UIImage

    let rating = aDecoder.decodeInteger(forKey: PropertyKey.ratingKey)

    // Must call designated initializer
    self.init(name: name, photo: photo, rating: rating)
}
  

3) В классе MealTableViewController я добавил функции saveMeals и loadMeals:

 func saveMeals() {
    let isSuccessfulSave = NSKeyedArchiver.archiveRootObject(meals, toFile: Meal.ArchiveURL.path)
    if !isSuccessfulSave {
        print("Failed to save meals...")
    }
}

func loadMeals() -> [Meal]? {
    return NSKeyedArchiver.classForKeyedUnarchiver().unarchiveObject(withFile: Meal.ArchiveURL.path) as? [Meal]
}
  

Код успешно компилируется, но во время выполнения происходит сбой. Строка 5 ошибки stacktrace, похоже, указывает на класс Meal и метод loadMeals в качестве виновника:

 5   FoodTracker 0x0000000107ce9e44 _TFC11FoodTracker23MealTableViewController9loadMealsfT_GSqGSaCS_4Meal__   276
6   FoodTracker 0x0000000107ce701a _TFC11FoodTracker23MealTableViewController11viewDidLoadfT_T_   266
7   FoodTracker 0x0000000107ce7222 _TToFC11FoodTracker23MealTableViewController11viewDidLoadfT_T_   34
  

К сожалению, на данный момент я не знаю больше, чтобы следить за панировочными сухарями, которые Xcode и Swift оставляют для меня. Просто пытаюсь узнать из источника (разработчики Apple).

Я выполнил поиск в Интернете и на этом сайте, но не совсем нашел ответ, который я смог бы использовать для решения проблемы. Это последний раздел руководства, и я действительно хотел бы его завершить. Спасибо вам за любую помощь, которую вы можете предоставить; Я надеюсь оплатить ее вперед, как только освоюсь с Swift. Спасибо!

Ответ №1:

Использовать NSKeyedUnarchiver :

 func loadMeals() -> [Meal]? {
    return NSKeyedUnarchiver.unarchiveObject(withFile: Meal.ArchiveURL.path) as? [Meal]
}
  

Комментарии:

1. Спасибо! Это именно то, что это было. Теперь, когда я просматриваю код руководства, я вижу, что NSKeyeedUnarchiver действительно указан… Должно быть, я выбрал неправильную опцию автозаполнения, а затем не смог определить разницу в ключевых словах. Я должен уделять больше внимания деталям кода, которые боюсь признать.