这似乎是一个简单的问题,但我正在努力在网上找到任何关于它的资源.

例如,我有以下代码:

import SwiftUI

struct ChildView: View {
    @Binding var parentText: String
    @State var childText: String = ""
    
    var body: some View {
        Form {
            TextField("Parent Text", text: $parentText)
            TextField("Child Text", text: $childText)
        }
    }
}

struct ParentView: View {
    @State var parentText: String = ""
    
    var body: some View {
        VStack {
            Text(parentText)
            ChildView(parentText: $parentText)
        }
    }
}

#Preview {
    ParentView()
}

为了测试,我运行了这段代码,并将子文本设置为"C",然后将父文本设置为"P".这样做很好,并导致视图中的P和C的形式.我想知道的是,为什么当我将父文本更新为"P"时,子文本没有设置为空字符串.

我对@State的理解是,当parentText发生更改时,ParentView主体将重新加载,这意味着它将重新初始化ChildView(),并且我也会预期ChildText也将被重新初始化为空字符串.显然,情况并非如此.

这一切为什么要发生?这只是SWIFT的优化,我一般不能依赖,还是这种行为是可预测的?如果它是可预测的,那么在什么条件下,子视图状态变量将保持它们的值?

推荐答案

在语言级别上,@State属性包装器被转换为如下所示(对于childText):

private var _childText: State<String> = State(wrappedValue: "")

var childText: String {
    get { _childText.wrappedValue }
    nonmutating set { _childText.wrappedValue = newValue }
}

var $childText: String {
    get { _childText.projectedValue }
}

这里唯一存储的属性是_childText,因此无论何时调用ChildView.init,都会运行初始化器调用State(wrappedValue: "").

正如Cheolhyun的回答所说,每个@State在内存中的某个地方都有自己的永久存储.当State(wrappedValue: "")第一次运行时,该持久存储被分配和初始化为具有初始值"",并且_childText.wrappedValue.projectedValue都引用该存储位置.

正如您所说的,当parentText个更新时,ParentView.body被判断,因此ChildView.initState(wrappedValue: "")再次运行.然而,这一次State(wrappedValue: "")没有分配新的存储空间.SwiftUI发现ChildView没有更改其identity,因此它重复使用相同的存储空间.它以_childText.wrappedValue.projectedValue都引用旧存储位置的方式来初始化_childText.

注意,上面提到的"identity"在SwiftUI中是一个非常重要的概念.WWDC视频"Demystifying SwiftUI"详细解释了identity是什么.

作为一个简单的例子,我们可以使用id修饰符来设置ChildView‘S身份.让我们让它的身份是parentText,这样每当parentText改变时,ChildView就改变它的身份.

ChildView(parentText: $parentText).id(parentText)

现在,当您更改parentText时,您可以看到子文本文本字段重置为空字符串.这两个文本字段也失go 了焦点,因为它们现在是两个与以前完全不同的文本字段,这是因为ChildView现在是一个与以前完全不同的ChildView,这是因为ChildView的身份改变了.

Swift相关问答推荐

VisionOS发送通知

SwiftData查询按日期排序的项的属性数组

Swift C外部实现的Task / Future类类型

如何将CodingKeys作为数组而不是枚举传递到类外部的函数中?

在Swift中如何将NSUrl转换为本地路径URL

如何从 RC 加载场景作为 ModelEntity 而不是普通实体?

在这个使用 URLSession 的简单 case 中是否创建了一个强引用循环?

如何自己实现同一个 iOS 16 锁屏圆形小部件?

如何从另一个 swift 文件中调用函数

Swift:创建用于调用 C 函数的指针数组

在 Swift 中实现自定义异步序列

调用从 SwiftUI 视图传递到 PageViewController.Coordinator 的函数

RxSwift:share() 替代方案,保证upstream 的单一订阅

Swift在十进制格式中失go 精度

从 NSData 对象在 Swift 中创建一个数组

CMutablePointer - 如何访问它?

用 UIBezierPath 画一条线

在 Swift 中指定 UITextField 的边框半径

Facebook SDK 4.0 IOS Swift 以编程方式注销用户

如何快速格式化用户显示(社交网络等)的时间间隔?