如何重写Swift结构体的Codable实现?以SIMD3为例
问题根源
首先得明确为什么你的扩展代码没生效:SIMD3[x,y,z]这样的数组)init(from:)和encode(to:)根本不会被调用——Swift会直接用原生的Codable逻辑。
解决方案
根据你的需求(不想用包装结构体,避免修改所有包含Vector3的对象),这里有两种更优雅的方案:
方案1:使用@propertyWrapper(推荐)
这种方式不需要修改任何现有结构体的Codable实现,只需要给Vector3类型的属性添加一个包装器,就能自动处理自定义JSON格式:
@propertyWrapper struct Vector3CodableWrapper: Codable { var wrappedValue: Vector3 enum CodingKeys: String, CodingKey { case x, y, z } // 初始化包装器 init(wrappedValue: Vector3) { self.wrappedValue = wrappedValue } // 自定义解码逻辑 init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) let x = try container.decodeIfPresent(Float.self, forKey: .x) ?? 0.0 let y = try container.decodeIfPresent(Float.self, forKey: .y) ?? 0.0 let z = try container.decodeIfPresent(Float.self, forKey: .z) ?? 0.0 self.wrappedValue = Vector3(x, y, z) } // 自定义编码逻辑 func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(wrappedValue.x, forKey: .x) try container.encode(wrappedValue.y, forKey: .y) try container.encode(wrappedValue.z, forKey: .z) } }
然后在你的结构体中这样使用:
struct Object1: Codable { @Vector3CodableWrapper var position: Vector3 var values: [Int] // 其他属性正常保留 }
这样Object1不需要任何自定义的Codable实现,编码后position会自动变成{"x":1,"y":2,"z":3}的格式。
方案2:创建封装SIMD3的自定义类型
如果你愿意修改项目中Vector3的类型定义,可以直接创建一个新的结构体,内部封装SIMD3<Float>,同时实现自定义Codable:
struct Vector3: Codable { private var simdValue: SIMD3<Float> // 暴露x/y/z属性,和原SIMD3用法一致 var x: Float { simdValue.x } var y: Float { simdValue.y } var z: Float { simdValue.z } enum CodingKeys: String, CodingKey { case x, y, z } // 兼容原SIMD3的初始化方式 init(_ x: Float, _ y: Float, _ z: Float) { self.simdValue = SIMD3(x, y, z) } // 自定义解码 init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) let x = try container.decodeIfPresent(Float.self, forKey: .x) ?? 0.0 let y = try container.decodeIfPresent(Float.self, forKey: .y) ?? 0.0 let z = try container.decodeIfPresent(Float.self, forKey: .z) ?? 0.0 self.simdValue = SIMD3(x, y, z) } // 自定义编码 func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(x, forKey: .x) try container.encode(y, forKey: .y) try container.encode(z, forKey: .z) } // 可选:添加simd库的运算符/方法转发,比如+/-等,保证用法和原SIMD3一致 static func + (lhs: Vector3, rhs: Vector3) -> Vector3 { Vector3(lhs.simdValue + rhs.simdValue) } }
这种方案是最彻底的,所有用到Vector3的地方都能直接使用自定义的Codable逻辑,不需要额外包装。缺点是需要修改项目中所有Vector3的类型引用,并且可能需要手动转发一些SIMD3的方法/运算符。
为什么你的原代码无效?
再强调一下:你用typealias Vector3 = SIMD3<Float>只是给SIMD3起了个别名,并不是创建新类型。当你扩展Vector3时,本质是扩展SIMD3<Float>,但SIMD3已经有官方的Codable实现,Swift会优先调用官方的实现,而忽略你在扩展中写的方法。这是Swift协议方法解析的既定规则,无法通过普通扩展绕过。
内容的提问来源于stack exchange,提问作者Runt8

