Swift 3:将数组保存到 UserDefaults 错误 - 对象数与键数不同

Posted

技术标签:

【中文标题】Swift 3:将数组保存到 UserDefaults 错误 - 对象数与键数不同【英文标题】:Swift 3: Saving Array to UserDefaults Error - Count of Objects Differs from Count of Keys 【发布时间】:2017-05-18 13:35:41 【问题描述】:

我的应用程序因错误而崩溃:

由于未捕获的异常“NSInvalidArgumentException”而终止应用程序,原因:“*** -[NSDictionary initWithObjects:forKeys:]:对象数 (0) 与键数 (1) 不同

这是我保存var favoriteAppAnnotations = [Int: AppAnnotation]()的方法:

let userDefaults = UserDefaults.standard
let favoritesData: Data = NSKeyedArchiver.archivedData(withRootObject: self.favoriteAppAnnotations)
userDefaults.set(favoritesData, forKey: "FAVORITES")
userDefaults.synchronize()

这是我的加载方式:

self.favoriteAppAnnotations = [:]
let userDefaults = UserDefaults.standard
if let decoded = userDefaults.object(forKey: "FAVORITES") as? Data 
   let favoritesArray = NSKeyedUnarchiver.unarchiveObject(with: decoded) as! [Int: AppAnnotation] // THIS IS WHERE THE CRASH IS
   if favoritesArray.isEmpty == false 
       self.favoriteAppAnnotations = favoritesArray
   

AppAnnotation 符合 NSCoding 如下:

    class AppAnnotation: ParentAppAnnotation, NSCoding // Note: ParentAppAnnotation is NSObject but doe snot conform to NSCoding
    
        open var placeId: Int?
        open var placeName: String?
        open var placeDescription: String?
        override init() 
            super.init()
        
        init(placeId: Int, placeName: String, placeDescription: String) 
            self.placeId = placeId
            self.placeName = placeName
            self.placeDescription = placeDescription
        
        required convenience init?(coder decoder: NSCoder) 
            guard let placeName = decoder.decodeObject(forKey: "placeName") as? String,
                  let placeDescription = decoder.decodeObject(forKey: "placeDescription") as? String
                  else  return nil 
            self.init(placeId: decoder.decodeInteger(forKey: "placeId"),
                    placeName: placeName,
                    placeDescription: placeDescription)

        
        func encode(with coder: NSCoder) 
            coder.encodeCInt(Int32(self.placeId!), forKey: "placeId")
            coder.encode(self.placeName, forKey: "placeName")
            coder.encode(self.placeDescription, forKey: "placeDescription")
        
    

我放弃了问题所在。考虑保存数组的替代方法...例如转换为JSON 字符串。 但我想了解当前的问题可能出在哪里。

【问题讨论】:

你为什么使用encodeCInt?应该是coder.encode(placeId!, forKey: "placeId") 顺便说一句,为什么不将 AppAnnotation 类属性声明为常量? 谢谢,我注意到整数编码。但是为什么类的属性应该是常量呢? 如果你需要改变它,只需创建一个新对象 BTW UserDefaults 有一个方法叫data(forKey:) 【参考方案1】:

强制展开的问题。

NSKeyedUnarchiver.unarchiveObject(with: decoded) as! [Int: AppAnnotation]

使用

NSKeyedUnarchiver.unarchiveObject(with: decoded) as? [Int: AppAnnotation]

查看方法定义:

open class func unarchiveObject(with data: Data) -> Any?

它返回可选值。

【讨论】:

为什么可以解决问题?它只会避免应用程序崩溃。 更改为 ?没有帮助。同样的错误。这可能是我构造数组的方式:[Int:AppAnnotation]?如果我只重做 [AppAnnotation] 会怎样? 除此之外,其他一切看起来都不错。您可以卸载该应用程序并重新安装。以前的持久数据可能存在问题。

以上是关于Swift 3:将数组保存到 UserDefaults 错误 - 对象数与键数不同的主要内容,如果未能解决你的问题,请参考以下文章

Swift - 将字典数组保存到 NSUserDefaults

Swift:将一个对象数组和另一个对象数组保存到 NSUserDefaults 中

使用 swift 将数组保存和检索到用户默认值

Swift - 将 [String: Any] 数组保存到 NSUserDefauls

将数组保存到 CoreData Swift

Swift - 视频文件数组可以很好地保存到应用程序库,但不能正确保存到应用程序文档目录