You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.19 09:16:36