从UserDefaults解码JSON数据赋值数组时App崩溃求助
崩溃原因分析
1. 单例初始化递归调用(直接崩溃原因)
在CreatedPlayers的私有初始化方法中,你调用了CreatedPlayers.loadPlayers(),而loadPlayers内部会调用AddPlayer方法,AddPlayer中又访问了shared.players。
由于shared是静态存储属性,其初始化是懒加载的——当第一次访问shared时才会执行CreatedPlayers()创建实例。但在实例的init方法执行过程中,还未完成实例创建与shared的赋值,此时AddPlayer访问shared会再次触发单例初始化,形成无限递归初始化,最终触发EXC_BREAKPOINT崩溃。
2. 额外的不良设计(加剧问题)
players数组的didSet会在每次修改时调用savePlayers,当loadPlayers循环添加玩家时,每添加一个就会触发一次保存操作,不仅效率低下,还会在初始化过程中增加递归复杂度。- 手动解析JSON完全没必要,
Player已实现Codable协议,直接用JSONDecoder解码更可靠,手动解析容易引入额外错误。
修复方案
方案1:调整初始化顺序,避免递归
修改CreatedPlayers的初始化逻辑,不在init中直接调用loadPlayers,而是在单例初始化完成后再加载数据:
class CreatedPlayers { static var shared = CreatedPlayers() var players: [Player] = [] { didSet { CreatedPlayers.savePlayers() } } private init(players: [Player] = []) { self.players = players // 移除init中的loadPlayers调用 } // 添加初始化完成后调用的加载方法 static func initialize() { loadPlayers() } // 恢复最初的Codable解码逻辑 static func loadPlayers() { guard let encodedData = UserDefaults.standard.data(forKey: "createdPlayers") else { print("No data found for key 'createdPlayers'") return } do { shared.players = try JSONDecoder().decode([Player].self, from: encodedData) } catch { print("Error decoding players: \(error.localizedDescription)") } } // 其余代码保持不变... }
然后在App启动时(比如AppDelegate的didFinishLaunchingWithOptions或SceneDelegate的sceneDidBecomeActive中)调用:
CreatedPlayers.initialize()
方案2:改用惰性加载加载数据(更简洁)
直接在players的初始化中加载数据,利用Swift惰性初始化特性,避免触发didSet(初始化赋值不会触发didSet):
class CreatedPlayers { static var shared = CreatedPlayers() var players: [Player] = { guard let encodedData = UserDefaults.standard.data(forKey: "createdPlayers") else { return [] } do { return try JSONDecoder().decode([Player].self, from: encodedData) } catch { print("Error decoding players: \(error.localizedDescription)") return [] } }() { didSet { CreatedPlayers.savePlayers() } } private init() {} // 移除原来的loadPlayers方法,其余代码保持不变... }
内容的提问来源于stack exchange,提问作者mickeyt
相关产品推荐
相关产品推荐

