译自 www.hackingwithswift.com/books/ios-s…
更多内容,欢迎关注公众号 「Swift花园」
喜欢文章?不如来个 🔺💛➕三连?关注专栏,关注我 🚀🚀🚀
Core Data:介绍
这个技术型项目的目标是进一步探索 Core Data,首先通过一些基本技术的总结开始,然后拆解更复杂的问题。
当你使用 Core Data 的时候,要记得这项技术已经有一定历史了 —— 它是在 Swift 诞生之前设计的,更别提 SwiftUI 了,因此在它和 Swift 协作时你很可能遭遇一些不如我们预期的部分。希望随着时间的推移,这种状况会被改善,但目前我们需要耐心处理这些部分。
对于 Core Data,有许多东西要探索,所以请创建一个全新的工程,取名 “CoreDataProject”。之所以不能叫 “CoreData” 是因为这会令 Xcode 搞不懂你的意图。确保你勾选 “Use Core Data” checkbox 以便 Xcode 为我们创建数据模型和 managed object context。
警告:Xcode 真的很容易忽略 Cord Data 编辑器中的改动,所以我习惯性在跳到别的文件之前按 Cmd+S 手动保存。如果这样也不管用,重启 Xcode!
> 译自 www.hackingwithswift.com/books/ios-s…
为什么 ForEach 可以使用 \.self
之前我们了解了使用ForEach来创建动态视图的不同方式,它们都有一个共同点:SwiftUI 需要知道如何唯一识别动态视图的每一项,以便正确地动画化改变。
如果一个对象遵循Identifiable协议,那么 SwiftUI 会自动使用它的id属性作为唯一标识。如果我们不使用Identifiable,那就需要指定一个我们知道是唯一的属性的 key path,比如图书的 ISBN 号。但假如我们不遵循Identifiable也没有唯一的 key path,我们通常会使用\.self。
我们对原始数据类型,例如Int和String使用过\.self,就像下面这样:
List {
ForEach([2, 4, 6, 8, 10], id: \.self) {
Text("\($0) is even")
}
}对于 Core Data 为我们生成托管类,我们也使用\.self,当时我没有解释这是如何工作的,或者说它究竟是如何与我们的ForEach关联的。不过今天我要再来讨论这个问题,因为我觉得这会给你提供一些有助益的洞察。
当我们使用\.self作为标识符时,我们指的是“整个对象”,但实践上这个指代并没有包含太多的含义 —— 一个结构体就是结构体,除了内容之外,它并没有包含任何特定的标识信息。因此,实际发生的事情是,Swift 计算了结构体的哈希值 —— 一种以固定长度的值表示复杂数据的方法 —— 然后以哈希值作为标识符。
哈希值可以以很多种方法生成,但所有方法的背后的概念是一致的:
- 无论输入的数据多大,输出总是固定大小。
- 对同一个对象计算两次哈希值,应该返回相同的值。
这两点听起来很简单,但你思考一下:假设我们获取 “Hello World” 的哈希值和莎士比亚全集的哈希值,两者都将是相同的大小。这意味着我们是无法从哈希值还原出原始数据的 —— 我们无法从 40 个看起来完全随机的十六进制数字转换出莎士比亚全集。
哈希常见于数据校验。举个例子,假如你下载了一个 8GB 的 zip 文件,你可以通过对比你本地的哈希值和服务器上的哈希值来确认文件是正确的 —— 如果两者匹配,说明 zip 文件是一致的。哈希还被用于字典的键和值,这是为什么字典查询速度很快的原因。
上面说的这些很重要,因为 Xcode 为我们生成托管对象的类时,它会让这些类遵循Hashable,这是一个表示 Swift 可以从中生成哈希值的协议,也就是说,我们可以用它的\.self作为标识符。这也是为什么String和Int可以用\.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。
警告: 虽然给一个对象计算两次哈希值应该返回相同的值,但在两次应用运行期间计算它们 —— 比如说,计算哈希值,退出应用,重启,然后再计算哈希值 —— 是有可能返回不同的值的。
我的公众号 这里有Swift及计算机编程的相关文章,以及优秀国外文章翻译,欢迎关注~