修改深度嵌套结构的简单方法



我对"写入时复制";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

有人有什么处理深度嵌套结构的好方法可以帮助我吗?还是我只能忍受上面第二个例子中的模式?

我最终得到的解决方案非常适合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

最新更新