为适配@AppStorage实现RawRepresentable导致JSONEncoder无限循环问题
问题原因与解决办法
这不是系统Bug,是你实现RawRepresentable时触发了无限递归,导致栈溢出崩溃。
崩溃根源
如果你的rawValue getter里直接用JSONEncoder().encode(self),JSON编码器在编码Printer实例时,会递归处理其所有属性逻辑;而因为Printer遵循RawRepresentable,编码过程会再次触发rawValue的获取,形成rawValue→编码self→rawValue的无限循环,最终耗尽栈空间崩溃。
解决办法
核心是把RawRepresentable的rawValue转换逻辑与结构体自身的编码逻辑解耦,避免循环调用。通过一个中间的可编码容器(元组或内部结构体)来中转属性,再进行编码:
示例实现(以RawValue为Data为例)
import Foundation import SwiftUI struct Printer: Codable, RawRepresentable { let name: String let id: UUID // 实际项目中的其他复杂属性 typealias RawValue = Data // 从rawValue解码实例 init?(rawValue: RawValue) { do { // 直接解码当前类型,解码过程不会触发rawValue调用,无递归风险 self = try JSONDecoder().decode(Self.self, from: rawValue) } catch { return nil } } // 生成rawValue的关键:用中间容器中转属性 var rawValue: RawValue { // 用元组包装所有属性,避免直接编码self let tempContainer = (name: name, id: id) do { return try JSONEncoder().encode(tempContainer) } catch { // 根据实际需求处理编码错误,这里返回空Data return Data() } } } // 测试与@AppStorage使用 let testPrinter = Printer(name: "Office Printer", id: UUID()) print(testPrinter.rawValue) // 正常输出Data,无递归 @AppStorage("savedPrinter") var savedPrinter: Printer? = nil
复杂结构体优化方案
如果你的Printer属性很多,推荐用内部结构体做中转,可读性更强:
struct Printer: Codable, RawRepresentable { let name: String let id: UUID let location: String let maxPaperSize: String typealias RawValue = String // 内部映射结构体,与Printer属性一一对应 private struct CodingWrapper: Codable { let name: String let id: UUID let location: String let maxPaperSize: String } init?(rawValue: RawValue) { guard let data = rawValue.data(using: .utf8) else { return nil } do { let wrapper = try JSONDecoder().decode(CodingWrapper.self, from: data) self.name = wrapper.name self.id = wrapper.id self.location = wrapper.location self.maxPaperSize = wrapper.maxPaperSize } catch { return nil } } var rawValue: RawValue { let wrapper = CodingWrapper(name: name, id: id, location: location, maxPaperSize: maxPaperSize) do { let data = try JSONEncoder().encode(wrapper) return String(data: data, encoding: .utf8) ?? "" } catch { return "" } } }
关键注意点
- 绝对不要在
rawValue的getter中直接编码self,必须通过中间容器中转属性 - 解码逻辑可以直接解析当前类型,因为解码过程不会触发
rawValue的获取,不会形成递归
内容的提问来源于stack exchange,提问作者vacawama
相关产品推荐
相关产品推荐

