游戏中通过Plist保存加载关卡数据异常问题求助
解决Plist保存关卡数据后台丢失的问题
老哥,我看你遇到的问题是关卡完成时SavedLevel的level能正常加1,但切后台后数据没存住——这在iOS用Plist存本地数据时挺常见的,我帮你从几个核心点排查修复:
1. 先确认你读写的是同一个Plist文件!
很多人踩坑在:从Bundle里读了初始Plist,但写入时又误写到Bundle路径了——而Bundle是只读的,根本写不进去!正确的姿势是把Plist复制到Documents目录(可读写区域),之后所有读写都用这个路径:
// 获取Documents下的Plist路径 func getGameProgressPlistPath() -> String { let docsDir = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] return docsDir + "/GameProgress.plist" } // 首次启动时复制Bundle里的模板Plist到Documents func copyTemplatePlistIfNeeded() { let targetPath = getGameProgressPlistPath() let fileManager = FileManager.default guard !fileManager.fileExists(atPath: targetPath) else { return } if let bundlePath = Bundle.main.path(forResource: "GameProgress", ofType: "plist") { try? fileManager.copyItem(atPath: bundlePath, toPath: targetPath) } }
启动App时先调用copyTemplatePlistIfNeeded(),之后读写全用getGameProgressPlistPath()返回的路径,别碰Bundle里的原文件。
2. 后台触发保存的逻辑要做对
iOS 13+之后,AppDelegate的后台方法可能被SceneDelegate接管了,你得确保在App进入后台时主动调用保存方法,甚至要处理后台任务超时的情况:
如果是用AppDelegate(iOS 12及以下):
func applicationDidEnterBackground(_ application: UIApplication) { SavedLevel.shared.saveCurrentLevel() }
如果是SceneDelegate(iOS 13+):
func sceneDidEnterBackground(_ scene: UIScene) { SavedLevel.shared.saveCurrentLevel() }
另外,系统给后台的执行时间有限,要是你的保存逻辑耗时(虽然Plist写入很快),最好加个后台任务延长时间:
func saveCurrentLevel() { var bgTask: UIBackgroundTaskIdentifier = .invalid bgTask = UIApplication.shared.beginBackgroundTask(withName: "SaveLevelProgress") { UIApplication.shared.endBackgroundTask(bgTask) bgTask = .invalid } // 执行写入逻辑 let plistPath = getGameProgressPlistPath() let progressDict = ["currentLevel": self.level] let writeSuccess = (progressDict as NSDictionary).write(toFile: plistPath, atomically: true) // 加个日志排查写入失败 if !writeSuccess { print("😅 Plist写入失败,检查路径或权限!") } UIApplication.shared.endBackgroundTask(bgTask) bgTask = .invalid }
3. 确保SavedLevel的模型和保存逻辑同步
检查你的SavedLevel单例里,保存时是不是真的用了最新的level值,还有初始化时有没有正确加载数据:
class SavedLevel { static let shared = SavedLevel() var level: Int = 1 private init() { // 启动时加载已保存的关卡 loadSavedLevel() } func loadSavedLevel() { let plistPath = getGameProgressPlistPath() if let progressDict = NSDictionary(contentsOfFile: plistPath) as? [String: Int] { self.level = progressDict["currentLevel"] ?? 1 } } func saveCurrentLevel() { // 这里一定要用self.level的最新值 let progressDict = ["currentLevel": self.level] _ = (progressDict as NSDictionary).write(toFile: getGameProgressPlistPath(), atomically: true) } // 关卡完成时调用这个方法 func completeLevel() { self.level += 1 // 最好完成后立即保存,别等后台 saveCurrentLevel() } }
4. 测试时别直接点Xcode的Stop按钮!
模拟器里直接点Stop会跳过后台生命周期方法,导致保存逻辑没执行。正确测试步骤:
- 按
⇧⌘H让App进入后台 - 等个2-3秒,让后台保存逻辑执行完
- 再终止App或者重新启动,看数据是否留存
要是还是有问题,你可以手动去Documents目录看Plist文件的内容:
- Xcode顶部菜单选
Window > Devices and Simulators - 选中你的设备/模拟器,找到你的App,点
Download Container - 解压容器后,在
AppData/Documents里找到你的Plist,打开看看值是不是对的
内容的提问来源于stack exchange,提问作者Matthew Fowler
相关产品推荐
相关产品推荐

