SwiftUI 常用 Property Wrappers 之 State

🧠🔋

属性包装器 State

作为老iOS开发,之前一直在强调面向对象开发,而后引申出来的就是对象内存管理,MRC和ARC什么的,Apple 在新的SwiftUI里,摒弃了这些,大量使用了存储在栈上的内存结构--> Struct 结构体,这样就不需要开发来管理内存了。

那么如果在 Struct 里不能直接修改一个变量的值,同样在我们的 View 里也不可以,下面的代码会报错。

struct StateView: View {
    var show: Bool = true
    var body: some View {
        Button {
            show.toggle()
        } label: {
            Text(show ? "Show" : "Hide")
        }
    }
}

❌ Cannot use mutating member on immutable value: 'self" is immutable

另外一个问题,当我们改变某个变量的时候,View 是如何更新内容的呢?这就是我们现在学习的一个 Property Wrapper, Swiftul 使用 @State 来允许我们修改一个 struct 里的变量,同时更新 View。更深入的理解是 @State 把struct 存储的变量转移到 SwiftUl 管理的内存里,SwiftUl 可以便捷的销毁和重建 View,非常高效用户不会感觉到整个过程,也不会丢失我们已经存在的状态。

struct StateView: View {
    @State var show: Bool = true
    var body: some View {
        Button {
            show.toggle()
        } label: {
            Text(show ? "Show" : "Hide")
        }
    }
}

上面的代码在运行的时候就不会报错了,当我们点击文字的时候,因为 show 的变化,Swiftul 会销毁掉已经显示的 Show 文字的 View 根据 show 的值而显示 Hide 文字的 View,达到更新 View 的目的。

有过开发经验的同学一眼就看出来了,这不就是双向绑定吗?更新模型,UI自动更新,更新UI,模型自动更新,就是这个意思。

更好的理解 State

struct StateView: View {
    @State var number: Int = 6
    var body: some View {
        VStack {
            Button {
                number += 1
            } label: {
                Text("数字 +1")
            }
            StateDetailView(number: number)
        }
    }
    
    struct StateDetailView: View {
        var number: Int
    //    @State var number: Int
        var body: some View {
            Text("当前数字: \(number)")
        }
    }
}

在上面的例子里,对于 StateDetaiView 的 number,没有加 @State 的时候随着我们点击按钮数字会增加,而增加了@State 反而不会增加了,这是为什么呢?

这就是因为上面最开始说的 @State 的原理,@State 把 struct 存储的变量转移到 SwiftUI 管理的内存里,当没有@State 的时候,当 number 更新的时候会更新 StateDetailView 的 number,而显示的数字就是这个 number,但是当添加了 @State 的时候,StateDetailView 的 number 就是由 SwiftUI 管理的 number,这个时候更新的时候没有更新SwiftUl 管理的 number。

由于 @State 属性包装器是为了在视图层次结构内共享状态而设计的,因此通常最好将其用于简单的视图。对于复杂的状态管理,我们可能需要使用其他属性包装器,如 @ObservedObject 或@EnvironmentObject,之后我们再介绍。