在AppStorage中设置自定义Struct值时,应用程序崩溃



我有一个自定义结构要存储在AppStorage:中

struct Budget: Codable, RawRepresentable {
enum CodingKeys: String, CodingKey {
case total, spent
}
var total: Double
var spent: Double
init(total: Double = 5000.0, spent: Double = 3000.0) {
self.total = total
self.spent = spent
}
init?(rawValue: String) {
guard let data = rawValue.data(using: .utf8),
let result = try? JSONDecoder().decode(Budget.self, from: data)
else { return nil }
self = result
}
var rawValue: String {
guard let data = try? JSONEncoder().encode(self),
let result = String(data: data, encoding: .utf8)
else {
return ""
}
return result
}
}

然后我有以下观点:

struct DemoView: View {
@AppStorage(UserDefaults.StorageKeys.budget.rawValue) var budget = Budget()
var body: some View {
Button("Update") {
budget.total = 10
}
}
}

当我点击按钮时,应用程序在Budget中的rawValueguard let data = try? JSONEncoder().encode(self)上的Thread 1: EXC_BAD_ACCESS崩溃。我在这里做错了什么?

您正在运行无限递归。这是因为同时符合EncodableRawRepresentable的类型会自动获得该encode(to:)实现(源(,该实现对原始值进行编码。这意味着当您调用JSONEncoder().encode时,它将尝试调用rawValue的getter,后者调用JSONEncoder().encode,从而形成无限递归。

为了解决这个问题,您可以显式地实现encode(to:)

func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(total, forKey: .total)
try container.encode(spent, forKey: .spent)
}

请注意,您还应该显式地实现init(from:),因为您还获得了一个init(from:)实现(源代码(,该实现试图将JSON解码为单个JSON字符串,而您肯定不希望这样做。

init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
total = try container.decode(Double.self, forKey: .total)
spent = try container.decode(Double.self, forKey: .spent)
}

最新更新