Swift:Xcode模拟器重启App后如何检查UserDefaults及排查Codable存储异常
我来帮你捋清楚这个问题,分两部分来解决:先教你怎么在模拟器里直观检查UserDefaults的变化,再帮你排查代码里可能踩的坑。
一、在Xcode模拟器中检查UserDefaults的变化
有两种简单的方法可以验证你的数据是否真的被存储,以及重启后是否能加载:
方法1:用Xcode调试控制台实时查看
这是最快捷的方式:
- 运行你的App,做一些会修改
machine对象的操作,然后让App进入后台(模拟器按Cmd+Shift+H,或者点击Home键) - 在Xcode里点击调试栏的暂停按钮(⏸️),暂停App运行
- 在控制台输入以下命令并回车:
控制台会打印出所有UserDefaults的键值对,你可以找到po UserDefaults.standard.dictionaryRepresentation()MachineStore.Key对应的条目,看看有没有二进制数据(如果有数据,会显示<some bytes>之类的内容) - 终止App(Xcode点击停止按钮🔴),重新运行App,再次暂停并执行上面的命令,对比是否能看到之前存储的
MachineStore.Key数据
方法2:直接查看模拟器的plist文件
如果你想更直观地看数据:
- 打开Xcode,点击顶部菜单
Window -> Devices and Simulators - 在弹出的窗口中,选中你正在使用的模拟器,找到你的App,点击Show Container
- 在打开的文件夹中,进入
Library/Preferences目录,找到以你的App Bundle ID命名的.plist文件(比如com.yourapp.YourApp.plist) - 双击打开这个plist文件,就能看到所有UserDefaults的键和对应的值,归档的二进制数据会显示为
<data>类型,你可以检查是否存在MachineStore.Key,以及重启App后这个条目是否还在
二、排查你的归档/加载代码的潜在问题
从你贴的代码来看,有几个可能导致不生效的点:
1. 检查类名是否匹配!
你存储的是self.machine,但解码时用的是VendingMachine.self——如果你的对象实际类型是Machine,那这里就会解码失败!
比如如果你的类定义是class Machine: Codable,那解码代码应该改成:
machine = try decoder.decode(Machine.self, from: data)
建议在catch块里打印更详细的错误信息,这样能快速定位问题:
catch { NSLog("解码失败:\(error)") // 会打印具体的错误,比如类型不匹配 }
2. 确保MachineStore.Key是全局唯一的
要给你的Key定义一个不会和其他键冲突的字符串,比如:
class MachineStore { static let Key = "com.yourcompany.yourapp.MachineStore" // ... 其他代码 }
如果Key定义重复或者不明确,存储和加载会找不到对应的数据。
3. 手动同步UserDefaults(保险起见)
虽然苹果官方说现在UserDefaults会自动同步,但在某些场景下(比如App被快速终止),手动同步能确保数据被写入磁盘:
在saveChanges()方法最后加上:
UserDefaults.standard.synchronize()
4. 确保saveChanges()被可靠调用
除了applicationDidEnterBackground,建议加上applicationWillTerminate方法,覆盖App被直接终止的场景:
func applicationWillTerminate(_ application: UIApplication) { machineStore.saveChanges() }
另外,可以在saveChanges()里加个打印,确认方法确实被调用了:
func saveChanges() { var data = Data() do { data = try encoder.encode(self.machine) NSLog("成功归档Machine数据,数据长度:\(data.count)") } catch { NSLog("归档失败:\(error.localizedDescription)") } UserDefaults.standard.set(data, forKey: MachineStore.Key) UserDefaults.standard.synchronize() }
5. 确保使用的是同一个MachineStore实例
如果你的App里创建了多个MachineStore对象,那加载的数据会存到一个实例里,而你使用的是另一个实例,自然看不到变化。建议把MachineStore改成单例模式:
class MachineStore { static let shared = MachineStore() // 全局单例 static let Key = "com.yourcompany.yourapp.MachineStore" var machine: VendingMachine! // 或者初始化一个默认值,比如VendingMachine() let encoder = JSONEncoder() let decoder = JSONDecoder() private init() {} // 防止外部创建实例 func saveChanges() { // ... 你的存储代码 } func loadMachine() { // ... 你的加载代码 } }
然后在AppDelegate里这样用:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { MachineStore.shared.loadMachine() return true } func applicationDidEnterBackground(_ application: UIApplication) { MachineStore.shared.saveChanges() } func applicationWillTerminate(_ application: UIApplication) { MachineStore.shared.saveChanges() }
这样整个App里用的都是同一个MachineStore实例,数据就能正确共享了。
内容的提问来源于stack exchange,提问作者undervine

