SwiftData 中的关系:变化与注意事项

SwiftData 中的关系:变化与注意事项

💡 原文中文,约9100字,阅读约需22分钟。
📝

内容提要

本文介绍了SwiftData在处理数据关系方面的变化和潜在的挑战。SwiftData允许开发者通过纯代码直接声明数据模型,改变了传统的“由里至表”方式。在处理关系数据时,开发者需要注意声明关系的可选性和默认值的设置。此外,使用数组来表示无序对多关系可能会导致性能问题,建议减少对数组的操作次数。开发者需要适应SwiftData的特点,灵活运用新的策略和技术,以充分利用其提供的便利并改善性能。

🔎

延伸解读

从“由里至表”到“由表及里”的转变

SwiftData 允许开发者通过纯代码声明数据模型,类型代码与数据模型同步生成,改变了 Core Data 时代先在模型编辑器中定义再生成代码的流程。这意味着模型严格遵循类型代码的声明,例如关系的可选性直接由类型后的问号决定。开发者需调整旧习惯,不能再像 Core Data 那样通过修改代码来优化模型映射。

关系默认值的误区与构造方法实践

SwiftData 中为关系设置默认值是一种误解。例如 var tag: Tag = Tag(name: "hello") 会在运行时出错,而 var items: [Item] = [] 并非有效默认值。在构造方法中创建关系数据是常见做法,但这同样不属于默认值,只是创建托管对象后的赋值操作。开发者应明确区分默认值与构造时赋值,避免错误预期。

逆向关系设置规则不明确,建议显式声明

SwiftData 对逆向关系的自动处理规则不如 Core Data 明确。实验表明,一对一和一对多关系中,若任意一端非可选,则需显式设置逆向关系;多对多关系则两端均为非可选时才需显式设置。为避免潜在问题,建议开发者始终使用 @Relationship(inverse:) 明确设置逆关系,而不是依赖自动推断。

数组操作多对关系的性能陷阱与优化

使用数组表示对多关系虽直观,但直接 append 操作性能较差。测试显示,添加 1000 个关系对象时,SwiftData 耗时 9.08 秒,而 Core Data 仅 0.012 秒。原因在于 SwiftData 需比较新旧数组以确定变更。优化方法是减少数组操作次数,如先收集对象再一次性 append(contentsOf:),可将耗时降低至 0.07 秒,但仍与 Core Data 有差距。

❓

Q&A

SwiftData 在数据模型声明方式上和 Core Data 有什么主要区别?

Core Data 需要先在 Xcode 数据模型编辑器中创建模型,再生成或编写 NSManagedObject 子类代码,是“由里至表”的流程;而 SwiftData 允许通过纯代码直接声明数据模型,类型代码和数据模型声明同步进行,SwiftData 会根据类型代码自动生成数据模型,转变为“由表及里”的方式。

在 SwiftData 中可以为关系设置默认值吗?

不可以。SwiftData 和 Core Data 一样,不支持为关系设置默认值。虽然代码中写 `var tag: Tag = Tag(name: "hello")` 看起来像默认值,但实际运行时会产生错误(Failed to find any currently loaded container for Tag),而 `var items: [Item] = []` 也不会为关系设置有效的默认值。在构造方法中创建关系数据同样不属于默认值。

SwiftData 中逆向关系需要显式设置吗?规则是什么?

SwiftData 对逆向关系的处理规则不如 Core Data 明确。实验表明:一对一关系中,两端都是 Optional 时自动添加逆向关系,若一端不为 Optional 则需显式设置;一对多关系中,两端都是 Optional 时自动设置,一端不为 Optional 时需显式设置;多对多关系中,两端都为 Optional 或任意一端为 Optional 时自动设置,若两端均为 Non-Optional 则需显式设置。建议始终显式设置逆关系以避免潜在问题。

SwiftData 使用数组表示对多关系有什么性能问题?如何优化?

使用数组操作对多关系(如 `item.tags.append(tag)`)在数据量大时性能显著劣化,因为 SwiftData 需要比较追加前后的数组来确定新增对象。例如创建 1000 个 Tag 时,Core Data 耗时 0.012 秒,SwiftData 耗时 9.080 秒。优化方法是减少对数组的操作次数,先将所有 Tag 保存在临时数组中,再一次性使用 `append(contentsOf:)` 添加,这样 1000 次循环的耗时可从 9.080 秒降至约 0.069 秒,效率提升约 130 倍。

SwiftData 在 Core Data with CloudKit 兼容性检查上有什么变化?

在 Core Data 中,只要在 Xcode 配置中勾选 Used with CloudKit,Xcode 就会自动检查数据模型是否符合 CloudKit 标准并给出提示。而 SwiftData 缺乏这种自动检查,开发者需要先为项目启用 CloudKit 功能并指定容器,只有在运行应用后若模型不符合规范才会收到错误通知。因此建议尽早设置 CloudKit 选项,避免未来大规模修改代码。

SwiftData 中在构造方法里创建关系数据有什么需要注意的?

在构造方法中创建关系数据(如 `tag = Tag(name: name)`)是一种常见且简洁的做法,这样创建的关系数据无需额外插入到 ModelContext 中。但需要注意,这并不等同于为关系设置默认值,它只是在创建托管对象实例后进行的赋值操作,不会在模型层面生成默认值。

➡️

继续阅读