SwiftUI Core Data:为什么 ForEach 可以使用 \.self ?

\color{red}{\Large \mathbf{Hacking \quad with \quad iOS: SwiftUI \quad Edition}}

{\Large \mathbf{Core \ Data}}

ForEach .self - 韦弦zhy

以前,我们研究了可使用ForEach创建动态视图的各种方式,但是它们都有一个共同点:SwiftUI需要知道如何唯一地标识每个动态视图,以便它可以正确地对更改进行动画处理。

如果对象符合Identifiable协议,则SwiftUI将自动使用其id属性进行唯一化。如果我们不使用Identifiable,则可以对已知独特的属性使用 keypath,例如书的ISBN号。但是,如果我们不符合Identifiable,并且没有唯一的keypath,则通常可以使用\.self

以前,我们将\.self用于基本类型,例如IntString,如下所示:

List {
    ForEach([2, 4, 6, 8, 10], id: \.self) {
        Text("\($0) is even")
    }
}

对于Core Data,我们使用\.self作为Xcode为我们生成的托管对象类。当时我没有解释它的工作原理,或者实际上它与广泛使用的ForEach有怎样的关系,但是现在值得讨论,因为我认为它将为您提供一些很好的见解。

当使用\.self作为标识符时,我们指的是“整个对象”,但实际上并不意味着太多——一个结构体就是一个结构体,因此除其内容外,它没有任何特定的标识信息。因此实际发生的是,Swift计算结构体的哈希值,这是一种以固定大小的值表示复杂数据的方法,然后将该哈希值用作标识符。

散列值可以通过多种方式生成,但是对于所有散列生成函数,其概念都是相同的:

  1. 无论输入大小如何,输出都应该是相同的固定大小。
  2. 连续两次为对象计算相同的哈希值,应返回相同的值。

这两个听起来很简单,但是请考虑一下:如果我们得到“Hello World”的哈希值和莎士比亚全集的哈希值,那么两者最终将具有相同的大小。这意味着无法将哈希转换回其原始值——我们无法将40个看似随机的十六进制字母和数字转换成莎士比亚的完整作品。

哈希通常用于诸如数据验证之类的事情。例如,如果您下载8GB的zip文件,则可以通过将文件的本地哈希值与服务器的本地哈希值进行比较来检查其是否正确——如果匹配,则表示该zip文件是相同的。哈希还与字典键和集合一起使用;这就是他们快速查找的方式。

所有这些都很重要,因为当Xcode为我们的托管对象生成一个类时,它使该类符合Hashable,这是一个协议,意味着Swift可以为其生成哈希值,这又意味着我们可以使用\.self作为标识符。这也是StringInt\.self一起使用的原因:它们也符合Hashable

Hashable有点像Codable:如果我们想使自定义类型符合Hashable,那么只要其中包含的所有内容也符合Hashable,那么我们就不需要做任何工作。为了证明这一点,我们可以创建一个符合Hashable而不是Identifiable的自定义结构体,并使用\.self进行标识:

struct Student: Hashable {
    let name: String
}

struct ContentView: View {
    let students = [Student(name: "Harry Potter"), Student(name: "Hermione Granger")]

    var body: some View {
        List(students, id: \.self) { student in
            Text(student.name)
        }
    }
}

我们可以使Student符合Hashable,因为它的所有属性都已经符合Hashable,因此Swift会计算每个属性的哈希值,然后将它们组合成一个代表整个结构体的哈希值。当然,如果我们最终招到两个同名学生,就会遇到问题,就像我们有两个相同字符串的字符串数组一样。

现在,您可能会认为这会导致问题:如果我们创建两个具有相同值的Core Data对象,它们将生成相同的哈希值,并且会遇到动画问题。但是,Core Data在这里确实做得很聪明:为我们创建的对象实际上除了我们在数据模型中定义的那些属性外,还具有其他属性的选择,包括一个称为对象ID的对象——该对象唯一的标识符,无论它包含什么属性。这些ID与UUID相似,尽管Core Data在创建对象时会按顺序生成它们。

因此,\.self适用于任何符合Hashable的条件,因为Swift会为对象生成哈希值,并使用该值唯一地标识它。这也适用于Core Data的对象,因为它们已经符合Hashable

警告:尽管连续两次为一个对象计算相同的哈希值应返回相同的值,但在应用程序的两次运行之间进行计算(即计算哈希值,退出应用程序,重新启动然后再次计算哈希值)可能会返回不同的值。

译自 Why does .self work for ForEach?

Core Data 项目——引言 Hacking with iOS: SwiftUI Edition 创建 NSManagedObject 子类

赏我一个赞吧~~~