我对Swift结构的“写时复制”行为越来越熟悉。我认为这是解决必须管理结构引用的一种很好的方法,但是在处理深度嵌套的结构时有点麻烦。
如果要更新深度嵌套的值,则需要该值的直接路径,以便可以在一行上进行修改:
myStruct.nestedArray[index].nestedValue = 1
编译器将复制myStruct.nestedArray[index]
并在该新值上将nestedValue
设置为1
。然后它将复制myStruct.nestedArray
并将新值设置为index
。然后它将复制myStruct
并用具有上述所有更改的新值替换以前的值。
这很好用,而且很酷,您只需一行代码即可完成操作,而不必担心之前引用过myStruct
及其子级的任何内容。但是,如果解析值路径时涉及到更复杂的逻辑,则该逻辑将变得更加冗长:
struct MyStruct {
var nestedEnum: MyEnum
}
enum MyEnum {
case one([NestedStruct])
case two([NestedStruct])
}
struct NestedStruct {
var id: Int
var nestedValue: Int
}
var myStruct = MyStruct(nestedEnum: .one([NestedStruct(id: 0, nestedValue: 0)]))
if case .one(var nestedArray) = myStruct.nestedEnum {
if let index = nestedArray.firstIndex(where: { $0.id == 0 }) {
nestedArray[index].nestedValue = 1
myStruct.nestedEnum = .one(nestedArray)
}
}
理想情况下,您可以执行以下操作:
if case .one(var nestedArray) = myStruct.nestedEnum {
if var nestedStruct = nestedArray.first(where: { $0.id == 0 }) {
nestedStruct.nestedValue = 1
}
}
但是一旦设置了nestedStruct.nestedValue
,就会吞噬nestedStruct
的新值。
如果Swift可以在函数外部使用inout
语义,那将是一个很好的选择,因此我可以先对nestedArray
进行“引用”,然后对其中的nestedStruct
进行设置内部nestedValue
,导致副本传播到myStruct
,其方式与我能够在一行中完成的方式相同。
有没有人有什么好方法可以处理深层嵌套的结构,从而可以在这里帮助我?还是我只需要忍受上面第二个示例中的模式?
答案 0 :(得分:1)
我最终得到的解决方案非常针对 SwiftUI,但它可能适用于其他框架。
基本上,我没有用一个单一的顶级方法来负责深度更新结构,而是安排了我的 SwiftUI 层次结构来镜像结构的结构,并将 Binding
向下传递,它只管理结构的一个节点层次结构。
例如,鉴于我上面定义的结构:
struct MyStruct {
var nestedEnum: MyEnum
}
enum MyEnum {
case one([NestedStruct])
case two([NestedStruct])
}
struct NestedStruct {
var id: Int
var nestedValue: Int
}
我可以这样做:
struct MyStructView: View {
@Binding var myStruct: MyStruct
var body: some View {
switch myStruct.nestedEnum {
case .one: OneView(array: oneBinding)
case .two: TwoView(array: twoBinding)
}
}
var oneBinding: Binding<[NestedStruct]> {
.init(
get: {
if case .one(array) = myStruct.nestedEnum {
return array
}
fatalError()
},
set: { myStruct.nestedEnum = .one($0) }
)
}
var twoBinding: Binding<[NestedStruct]> { /* basically the same */ }
}
struct OneView: View {
@Binding var array: [NestedStruct]
var body: some View {
ForEach(0..<array.count, id: \.self) {
NestedStructView(nestedStruct: getBinding($0))
}
}
func getBinding(_ index: Int) -> Binding<NestedStruct> {
.init(get: { array[index] }, set: { array[index] = $0 })
}
}
struct NestedStructView: View {
@Binding var nestedStruct: NestedStruct
var body: some View {
NumericInput(title: "ID: \(nestedStruct.id)", value: valueBinding)
}
var valueBinding: Binding<Int> {
.init(get: { nestedStruct.value }, set: { nestedStruct.value = $0 })
}
}
唯一令人讨厌的一点是手动构造 Binding
可能有点冗长。我希望 SwiftUI 有一些语法可以从包含数组或结构的 Binding
中获取嵌套的 Binding
。