如何用UserDefaults保存包含自定义对象的自定义对象?
别担心,这种嵌套自定义对象的存储其实就是单个自定义对象存储的延伸——只要让你嵌套的StatsIntervalModel也遵守NSCoding协议,实现对应的编码解码逻辑,外层的ConfigLabelMainList就能正常保存到UserDefaults里。我给你一步步拆解实现:
第一步:让嵌套类
StatsIntervalModel遵守NSCoding 首先必须确保你的StatsIntervalModel继承NSObject并实现NSCoding的两个核心方法:encode(with:)(编码)和required init?(coder:)(解码)。假设这个类有几个示例属性,你可以参考下面的写法调整成自己的属性:
class StatsIntervalModel: NSObject, NSCoding { // 示例属性,替换成你自己的属性 var interval: TimeInterval var displayName: String // 自定义初始化器 init(interval: TimeInterval, displayName: String) { self.interval = interval self.displayName = displayName } // 编码方法:把属性写入NSCoder func encode(with coder: NSCoder) { coder.encode(interval, forKey: "interval") coder.encode(displayName, forKey: "displayName") } // 解码构造器:从NSCoder读取属性,注意安全的类型转换 required init?(coder: NSCoder) { guard let name = coder.decodeObject(forKey: "displayName") as? String else { return nil // 读取失败则返回nil } self.interval = coder.decodeDouble(forKey: "interval") self.displayName = name } }
第二步:完善外层类
ConfigLabelMainList的NSCoding实现 接下来回到你的ConfigLabelMainList类,在它的编码和解码方法里,直接对每个StatsIntervalModel属性调用编码/解码方法即可——因为我们已经让内层类支持NSCoding了,系统会自动递归处理:
class ConfigLabelMainList: NSObject, NSCoding { var labelMiddleFirst: StatsIntervalModel var labelMiddleSecond: StatsIntervalModel var labelMiddleThird: StatsIntervalModel var labelRightFirst: StatsIntervalModel var labelRightSecond: StatsIntervalModel // 你的自定义初始化器 init(labelMiddleFirst: StatsIntervalModel, labelMiddleSecond: StatsIntervalModel, labelMiddleThird: StatsIntervalModel, labelRightFirst: StatsIntervalModel, labelRightSecond: StatsIntervalModel) { self.labelMiddleFirst = labelMiddleFirst self.labelMiddleSecond = labelMiddleSecond self.labelMiddleThird = labelMiddleThird self.labelRightFirst = labelRightFirst self.labelRightSecond = labelRightSecond } // 编码方法:逐个编码嵌套的自定义对象 func encode(with coder: NSCoder) { coder.encode(labelMiddleFirst, forKey: "labelMiddleFirst") coder.encode(labelMiddleSecond, forKey: "labelMiddleSecond") coder.encode(labelMiddleThird, forKey: "labelMiddleThird") coder.encode(labelRightFirst, forKey: "labelRightFirst") coder.encode(labelRightSecond, forKey: "labelRightSecond") } // 解码构造器:逐个解码嵌套对象,确保类型转换安全 required init?(coder: NSCoder) { guard let middleFirst = coder.decodeObject(forKey: "labelMiddleFirst") as? StatsIntervalModel, let middleSecond = coder.decodeObject(forKey: "labelMiddleSecond") as? StatsIntervalModel, let middleThird = coder.decodeObject(forKey: "labelMiddleThird") as? StatsIntervalModel, let rightFirst = coder.decodeObject(forKey: "labelRightFirst") as? StatsIntervalModel, let rightSecond = coder.decodeObject(forKey: "labelRightSecond") as? StatsIntervalModel else { return nil } self.labelMiddleFirst = middleFirst self.labelMiddleSecond = middleSecond self.labelMiddleThird = middleThird self.labelRightFirst = rightFirst self.labelRightSecond = rightSecond } }
第三步:保存和读取到
UserDefaults 最后就是把ConfigLabelMainList实例归档成Data,再存入UserDefaults,读取时反向操作即可:
// 保存配置到UserDefaults func saveConfigToUserDefaults() { // 先创建测试用的StatsIntervalModel实例 let interval1 = StatsIntervalModel(interval: 3600, displayName: "1小时") let interval2 = StatsIntervalModel(interval: 86400, displayName: "1天") let interval3 = StatsIntervalModel(interval: 604800, displayName: "1周") let interval4 = StatsIntervalModel(interval: 2592000, displayName: "1个月") let interval5 = StatsIntervalModel(interval: 31536000, displayName: "1年") let config = ConfigLabelMainList(labelMiddleFirst: interval1, labelMiddleSecond: interval2, labelMiddleThird: interval3, labelRightFirst: interval4, labelRightSecond: interval5) // 归档成Data guard let configData = try? NSKeyedArchiver.archivedData(withRootObject: config, requiringSecureCoding: false) else { print("归档失败") return } UserDefaults.standard.set(configData, forKey: "AppConfigLabelMainList") } // 从UserDefaults读取配置 func loadConfigFromUserDefaults() -> ConfigLabelMainList? { guard let configData = UserDefaults.standard.data(forKey: "AppConfigLabelMainList") else { return nil } return try? NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(configData) as? ConfigLabelMainList }
关键注意点
- 只要是嵌套的自定义对象,必须全部遵守
NSCoding协议,不管嵌套多少层,系统都会递归处理编码和解码。 - 解码时一定要做安全的类型转换,用
guard或if let避免因为类型不匹配导致崩溃。 - 如果你的属性是可选类型,在解码时要对应处理(比如用
decodeObject(forKey:)直接赋值给可选属性)。
内容的提问来源于stack exchange,提问作者PaFi
相关产品推荐
相关产品推荐

