阅读 107

[SwiftUI 100天] Core Data 创建 NSManagedObject 对象

译自 www.hackingwithswift.com/books/ios-s…
更多内容,欢迎关注公众号 「Swift花园」
喜欢文章?不如来个 🔺💛➕三连?关注专栏,关注我 🚀🚀🚀

创建 NSManagedObject 子类

当我们创建一个新的 Core Data 实体,Xcode 会在我们编译代码时为我们自动生成一个托管对象类。然后我们在 SwiftUI 中用@FetchRequest在我们的 UI 上显示数据,不过正如你所见,这个过程有些痛苦:有大量的可选型需要解包,所以你需要忍受空合运算法,以便代码能正常工作。

对于这个问题,有两个解决方案:其中一个又快又简单的方案有时候会出现问题,另一个稍微慢一点的方案长期看来工作的更好。

首先,让我们创建一个实体:打开你的数据模型,然后创建一个叫 Movie 的实体,包括属性 “title”(字符串), “director”(字符串)和 “year” (一个 16 位整数)。在你离开数据模型编辑器之前,我要你点击 View 菜单,选择 Inspectors > Show Data Model Inspector,这会在 Xcode 的右边调起一个面板,里面包含你所选择的任何实体的更多信息。

当你选择 Movie 时,你会看到该视图的各种数据模型选项,我要你特别留意 “Codegen” 这一项,它控制了 Xcode 是如何在我们编译项目时生成实体的托管对象类。默认情况下,它会是 Class Definition。我将把它改成 Manual/None,这样一来我们自己就拥有了这个类生成的完全控制权。

现在 Xcode 已经不再自动生成Movie类,除非我们自己构建这个类,否则无法使用它。回到 Editor 菜单,选择 Create NSManagedObject Subclass,确保 “CoreDataProject” 被选中,然后点击 Next,然后确保 Movie 被选中,再次点击 Next。你可能会想问 Xcode 要在哪里保存这个类的代码,请你确保选中左边有一个黄色文件夹图标的 “CoreDataProject”,并选中 CoreDataProject 文件夹。当这些都准备好之后,点击Create 完成创建过程。

我们刚才做的操作是让 Xcode 把自动生成的代码转换成我们可见的 Swift 文件。记住一件事,如果你手工改动了 Xcode 为我们生成的文件,那么重新生成时你的改动会丢失。

Xcode 会为我们生成两个文件,但我们只关心其中的一个:Movie+CoreDataProperties.swift。在那个文件里,你会看到下面三行代码:

@NSManaged public var title: String?
@NSManaged public var director: String?
@NSManaged public var year: Int16复制代码

在这一小段代码中你会发现三件事:

  1. 这是可选型代码问题产生的根源
  2. year 不是可选型,意味着 Core Data 会为我们假定一个默认值
  3. year 不是可选型,意味着 Core Data 会为我们假定一个默认值

@NSManaged 并不是一个属性包装器 —— 它比 SwiftUI 的属性包装器要古老的多。实际上,它其实稍稍揭示了 Core Data 内部的工作机制:相对于直接存在于类中作为属性,它们实际上是由 Core Data 存储在一个字典中,由 Core Data 读取和写入。当我们读取或者写入标记了@NSManaged的属性时,Core Data 缓存这些操作,然后在内部处理 —— 远非一个简单的 Swift 字符串。

现在,当你凝视着代码,然后在想“我可不想要这些可选型”,然后把代码改成下面这样:

@NSManaged public var title: String
@NSManaged public var director: String
@NSManaged public var year: Int16复制代码

你知道吗?这么干绝对还能工作。你创建Movie对象的代码和之前一样,用 fetch 请求来查询它们,保存它们的托管对象上下文,等等,这一切都没有问题。

但是,你可能会注意有些事情变得有点奇怪:尽管我们的属性都已经不是可选型了,我们仍然可以不提供任何值而创建出一个Movie类的实例。这本来应该是不可能的:这些属性并不是可选型,意味着它们必须一直有值,而我们却可以不给值创建它们。

发生的事情可以透露出@NSManaged的魔法,这些并非实际的属性,结果@NSManaged让我们做到了本不能做到的事情。事实上,这么做是优雅的,尤其对于小型的 Core Data 项目或者对于学习者来说。但是,有一个更深层次的问题:Core Data 是懒加载的。

还记得 Swift 的lazy关键字吧,它可以让我们延迟实际的初始化,直到我们实际需要用到的时刻。Core Data 的做法差不多,只是针对数据:有的时候,有些数据看似已经加载了,但实际上没有。因此 Core Data 会尝试最小化它的内存占用。Core Data 把这值称为堕值,取义 “断层” —— 一条介于某样东西实际存在和某样东西只是占位符的断层。

对于这些堕值,我们并不需要做什么实际的工作,因为只要我们尝试读取这些值 Core Data 会透明地为我们获取实际的数据并且返回给我们 —— 这是@NSManaged的又一好处。不过,当我们开始在这些 Core Data 的属性上游走的时候,我们要承担暴露其软肋的风险。这些东西特别不符合 Swift 预期的工作方式,如果我们尝试回避这个事实,那我们很可能会引入问题 —— 某些我们认为绝对不会是 nil 的值可能在某个时刻突然变成 nil。

因此,为了帮助我们更安全地访问这些可选值,你可能会考虑添加一些计算属性,这也能帮助我们把所有的空合操作符放在一个地方处理。例如,给Movie添加下面这个属性,以确保我们总是有一个合法的标题字符串可以使用:

public var wrappedTitle: String {
    title ?? "Unknown Title"
}复制代码

这样一来,你的代码的其余部分,就不用担心 Core Data 的可选性,如果你想要改变默认值,可以在单个文件里就解决掉。


我的公众号 这里有Swift及计算机编程的相关文章,以及优秀国外文章翻译,欢迎关注~

                                                                   


关注下面的标签,发现更多相似文章
评论