Swift如何通过变量名或索引动态修改Codable结构体的属性值
方案1:预定义KeyPath映射实现(类型安全、性能最优)
这种方案和硬编码性能完全一致,编译期即可做错误校验,是首选实现方式。
你只需要给Effects结构体扩展预定义属性映射和下标支持即可:
extension Effects { // 按你约定的索引顺序,预存所有ValueAndRange?类型属性的可写KeyPath static let valuePropertyKeyPaths: [WritableKeyPath<Effects, ValueAndRange?>] = [ \.dryWetMix, \.resonance, \.cutoffFrequency // 后续新增属性只需按顺序追加到此处即可 ] // 如需按属性名字符串访问,额外增加名称映射表 static let valuePropertyNameMap: [String: WritableKeyPath<Effects, ValueAndRange?>] = [ "dryWetMix": \.dryWetMix, "resonance": \.resonance, "cutoffFrequency": \.cutoffFrequency ] // 下标支持:通过索引访问属性 subscript(valueIndex index: Int) -> ValueAndRange? { get { guard index >= 0, index < Self.valuePropertyKeyPaths.count else { return nil } return self[keyPath: Self.valuePropertyKeyPaths[index]] } set { guard index >= 0, index < Self.valuePropertyKeyPaths.count else { return } self[keyPath: Self.valuePropertyKeyPaths[index]] = newValue } } // 下标支持:通过属性名字符串访问属性 subscript(valueName name: String) -> ValueAndRange? { get { guard let keyPath = Self.valuePropertyNameMap[name] else { return nil } return self[keyPath: keyPath] } set { guard let keyPath = Self.valuePropertyNameMap[name] else { return } self[keyPath: keyPath] = newValue } } }
调用方式完全符合你的需求:
let setThisValue = 0.6 // 按索引访问 let effectProppertyIndex = 2 setConfig.instrumentsConfig[0].effects![0][valueIndex: effectProppertyIndex]?.setValue(value: setThisValue) // 按属性名字符串访问 let effectPropperty: String = "resonance" setConfig.instrumentsConfig[0].effects![0][valueName: effectPropperty]?.setValue(value: setThisValue)
方案2:反射+Codable中转实现(无需维护映射表、灵活性更高)
如果你的属性变动非常频繁,不想每次新增属性都更新映射表,可以用这种动态实现方案,缺点是有编解码的性能开销,适合调用频率不高的场景:
extension Effects { mutating func setValue(_ value: Double, forPropertyName name: String) throws { // 序列化为字典修改 var dict = try JSONSerialization.jsonObject(with: JSONEncoder().encode(self)) as? [String: Any] ?? [:] if var propDict = dict[name] as? [String: Any] { propDict["value"] = value dict[name] = propDict } // 重新解码为结构体实例 self = try JSONDecoder().decode(Effects.self, from: JSONSerialization.data(withJSONObject: dict)) } }
调用方式:
let setThisValue = 0.6 let effectPropperty: String = "resonance" try? setConfig.instrumentsConfig[0].effects![0].setValue(setThisValue, forPropertyName: effectPropperty)
内容的提问来源于stack exchange,提问作者Frans-Jan Wind
相关产品推荐
相关产品推荐

