在UserDefaults中保存自定义对象数组时触发NSInvalidArgumentException异常
这个错误我太熟悉了!之前刚学Swift的时候踩过一模一样的坑😅。原因很简单:UserDefaults只接受Apple官方定义的「属性列表(Property List)」类型,自定义对象直接往里塞肯定会触发这个崩溃。给你两种最常用的解决办法,按需选就行:
方法一:用Swift Codable协议(推荐,代码更简洁)
这是Swift 4+推出的序列化方案,比传统的NSCoding省心太多:
- 第一步:让你的自定义对象遵循
Codable协议(如果属性都是基础类型或已经遵循Codable的类型,直接加协议就行,不用写额外代码)
struct CustomModel: Codable { var name: String var age: Int // 其他自定义属性,比如String、Int、Double、Date这些都自动支持 }
- 第二步:存储数组时,先把数组转成
Data再存到UserDefaults
// 假设你有一个自定义对象数组 let myCustomArray = [CustomModel(name: "Alice", age: 28), CustomModel(name: "Bob", age: 32)] do { // 把数组编码成Data let encodedData = try JSONEncoder().encode(myCustomArray) // 存入UserDefaults UserDefaults.standard.set(encodedData, forKey: "SavedCustomArray") } catch { print("存储失败:\(error.localizedDescription)") }
- 第三步:读取时把Data解码回对象数组
if let savedData = UserDefaults.standard.data(forKey: "SavedCustomArray") { do { // 解码成自定义对象数组 let savedArray = try JSONDecoder().decode([CustomModel].self, from: savedData) // 这里就可以用savedArray做你需要的操作了 print(savedArray) } catch { print("读取失败:\(error.localizedDescription)") } }
方法二:用NSCoding协议(兼容Objective-C老项目)
如果你的项目是OC和Swift混编,或者需要兼容老代码,可以用这个传统方案:
- 第一步:自定义类要继承
NSObject并遵循NSCoding协议,实现编码和解码方法
class CustomModel: NSObject, NSCoding { var name: String var age: Int init(name: String, age: Int) { self.name = name self.age = age super.init() } // 编码:把对象属性转成可存储的格式 func encode(with coder: NSCoder) { coder.encode(name, forKey: "modelName") coder.encode(age, forKey: "modelAge") } // 解码:从存储的数据里还原对象属性 required init?(coder: NSCoder) { name = coder.decodeObject(forKey: "modelName") as? String ?? "" age = coder.decodeInteger(forKey: "modelAge") super.init() } }
- 第二步:存储数组时用
NSKeyedArchiver转成Data
let myCustomArray = [CustomModel(name: "Alice", age: 28), CustomModel(name: "Bob", age: 32)] do { let encodedData = try NSKeyedArchiver.archivedData(withRootObject: myCustomArray, requiringSecureCoding: false) UserDefaults.standard.set(encodedData, forKey: "SavedCustomArray") } catch { print("存储失败:\(error.localizedDescription)") }
- 第三步:读取时用
NSKeyedUnarchiver解码
if let savedData = UserDefaults.standard.data(forKey: "SavedCustomArray") { do { if let savedArray = try NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(savedData) as? [CustomModel] { // 使用还原后的数组 print(savedArray) } } catch { print("读取失败:\(error.localizedDescription)") } }
补充说明:为什么会报错?
UserDefaults支持的属性列表类型只有这些:
- 基础类型:
String、Int、Double、Bool(对应OC的NSString、NSNumber) - 日期:
Date(NSDate) - 数据:
Data(NSData) - 集合:
Array、Dictionary(里面的元素也必须是上述类型)
自定义对象不属于这个范围,所以必须先序列化成Data,才能存入UserDefaults。
内容的提问来源于stack exchange,提问作者Paul
相关产品推荐
相关产品推荐

