提问人:mickeyt 提问时间:11/10/2023 最后编辑:HangarRashmickeyt 更新时间:11/11/2023 访问量:80
从 UserDefaults 解码 JSON 数据并将解码的数据分配给数组时应用崩溃
App crashing when decoding JSON data from UserDefaults and assigning decoded data to an array
问:
我是学习编码的新手。我给自己设定了一个挑战,即创建一个应用程序,并一直在关注书籍,并使用 ChatGPT 来帮助为我目前不知道该怎么做的事情创建代码。设法让我的应用程序正常工作,当它在填充数组时打开时,我会创建一些数据,但是我要求它帮助我下一步在设备上读取和写入数据。它帮助我编写的代码说应该可以工作,但是当应用程序启动时,我总是崩溃。下面是两个对象和函数,用于处理保存数据和在启动时加载数据:
struct Player : Codable {
let name: String
var handicap: Int
let userID: String
private init(name: String, handicap: Int, userID: String) {
self.name = name
self.handicap = handicap
self.userID = userID
}
static func createNewPlayer(name: String, handicap: Int) -> Player {
let uuid = UUID().uuidString
let newPlayer = Player(name: name, handicap: handicap, userID: uuid)
return newPlayer
}
static func createNewPlayer(name: String, handicap: Int, userID: String) -> Player {
let newPlayer = Player(name: name, handicap: handicap, userID: userID)
return newPlayer
}
}
class CreatedPlayers {
static var shared = CreatedPlayers()
var players: [Player] = [] {
didSet {
CreatedPlayers.savePlayers()
}
}
private init(players: [Player] = []) {
self.players = players
CreatedPlayers.loadPlayers()
}
static func savePlayers() {
do {
let encodedData = try JSONEncoder().encode(shared.players)
UserDefaults.standard.set(encodedData, forKey: "createdPlayers")
} catch {
print("Error encoding players: \(error.localizedDescription)")
}
}
static func loadPlayers() {
//if let encodedData = UserDefaults.standard.data(forKey: "createdPlayers") {
//if let jsonString = String(data: encodedData, encoding: .utf8) {
//print("JSON String: \(jsonString)")
//do {
//shared.players = try JSONDecoder().decode([Player].self, from: encodedData)
//} catch let decodingError {
//print("Error decoding players: \(decodingError)")
//}
//} else {
//print("No data found")
//}
//}
if let encodedData = UserDefaults.standard.data(forKey: "createdPlayers") {
do {
let json = try JSONSerialization.jsonObject(with: encodedData, options: [])
if let playerDataArray = json as? [[String: Any]] {
// Process each player dictionary
//var parsedPlayers: [Player] = []
for playerData in playerDataArray {
if
let userID = playerData["userID"] as? String,
let name = playerData["name"] as? String,
let handicap = playerData["handicap"] as? Int {
let player = Player.createNewPlayer(name: name, handicap: handicap, userID: userID)
self.AddPlayer(player: player)
}
}
} else {
print("Error: Unable to parse player data from JSON")
}
} catch {
print("Error decoding players: \(error.localizedDescription)")
}
} else {
print("No data found for key 'createdPlayers'")
}
}
static func AddPlayer(player: Player) {
shared.players.append(player) //*THE CRASH HAPPENS HERE*
}
static func PlayerCount() -> Int {
return shared.players.count
}
static func RetrievePlayers(keyValue: String) -> Player? {
if let retrievedPlayer = shared.players.first(where: { $0.userID == keyValue}) {
return retrievedPlayer
} else {
return nil
}
}
static func RetrievePlayersWithIndex(index: Int) -> Player? {
if index >= 0 && index < shared.players.count {
return shared.players[index]
} else {
return nil
}
}
static func DeletePlayer(userID: String) {
shared.players.removeAll(where: { $0.userID == userID})
}
}
loadPlayers() 中所有注释掉的部分都是因为我首先使用了内置的解码方法,但是当它崩溃时,我试图手动解码数据并手动创建一个新播放器,所以看看这是否有所作为(根据我知道所有这些函数在从应用程序本身触发时都可以正常工作), 但它仍然崩溃了,我什至无法捕获错误消息。
当我调用自己的函数时,会发生崩溃,该函数将新玩家添加到玩家数组中,以便在应用程序启动后,有一个数组,其中包含写入设备的相同玩家数据。
调试器中的错误只是说:
线程 1:EXC_BREAKPOINT(代码=1,子代码=0x1013ee3f4)
有人可以帮我理解为什么会这样吗?
答:
我自己对此进行了故障排除,并认为这可能与 CreatedPlayers 在开始被要求提供数据之前从未初始化有关(例如,当第一个应用程序屏幕上的表格视图开始加载信息时的玩家计数)。当我考虑应用程序中事件的顺序时,init 函数从未被调用,loadPlayers() 函数就位于此处。
我将对 loadPlayers() 的调用移动到处理 tableview 填充的视图控制器中的 viewDidLoad 函数,删除了 CreatedPlayers init 函数中的调用,它有效!根据 HangarRash 之前的评论,还将解码功能更改为 JSONDecoder。
所以这与解码不起作用无关;这与我用来存储保存的玩家信息的对象有关,该对象从未被初始化,因此在我开始向它询问数据之前,里面的数组也没有初始化。
这是更正后的代码 - 如果我认为 viewDidLoad 函数与它有任何关系,我最初会包含它。
struct Player : Codable {
let name: String
var handicap: Int
let userID: String
private init(name: String, handicap: Int, userID: String) {
self.name = name
self.handicap = handicap
self.userID = userID
}
static func createNewPlayer(name: String, handicap: Int) -> Player {
let uuid = UUID().uuidString
let newPlayer = Player(name: name, handicap: handicap, userID: uuid)
return newPlayer
}
static func createNewPlayer(name: String, handicap: Int, userID: String) -> Player {
let newPlayer = Player(name: name, handicap: handicap, userID: userID)
return newPlayer
}
}
class CreatedPlayers {
static var shared = CreatedPlayers()
var players: [Player] = [] {
didSet {
CreatedPlayers.savePlayers()
}
}
private init(players: [Player] = []) {
self.players = players
//CreatedPlayers.loadPlayers()
}
static func savePlayers() {
do {
let encodedData = try JSONEncoder().encode(shared.players)
UserDefaults.standard.set(encodedData, forKey: "createdPlayers")
} catch {
print("Error encoding players: \(error.localizedDescription)")
}
}
static func loadPlayers() {
if let encodedData = UserDefaults.standard.data(forKey: "createdPlayers") {
if let jsonString = String(data: encodedData, encoding: .utf8) {
print("JSON String: \(jsonString)")
do {
shared.players = try JSONDecoder().decode([Player].self, from: encodedData)
} catch let decodingError {
print("Error decoding players: \(decodingError)")
}
} else {
print("No data found")
}
}
}
class ViewController: UIViewController, AddPlayerDelegate {
@IBOutlet weak var PlayerTableView: UITableView!
@IBOutlet weak var AddPlayerPlusBarButton: UIBarButtonItem!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
self.PlayerTableView.dataSource = self
self.PlayerTableView.isEditing = false
CreatedPlayers.loadPlayers() //Added this to ensure array is initialised and players loaded before subsequent requests for player count and player data
}
谢谢 HangerRash, lorem ipsum 的回答。现在将查看核心数据,而不是 UserDefaults!
上一个:按下按钮时创建新数组
评论
JSONEncoder
JSONSerialization
JSONDecoder