概述
各位似秃似不秃小码农们都知道,在苹果众多开发平台中 CoreData 无疑是那个最简洁、拥有“官方认证”且最具兼容性的数据库框架。使用它可以让我们非常方便的搭建出 App 所需要的持久存储体系。
不过,大家是否知道在 CoreData 中还存在一个 Transformable 类型,它到底是个啥?应用场景有哪些?在最新的 SwiftData 中有没有对应物?对于开发者又有哪些“见雀张罗”的撸码陷阱和最佳实践呢?
在本篇博文中,您将学到如下内容:
- 概述
- 1. 查无此“类”:奇怪的 Transformable 类型
- 2. Transformable 存在的真谛
- 3. 派生自 NSObject 的内置类
- 3.1 数组(Array)
- 总结
本系列文章一共包括将近 3w 枚机智而幽默的文字、详实的大段代码示例以及海量图片,定能让小伙伴们对 Transformable 类型的“驾驭”更加胸有成竹、胜券在握!
那还等什么呢?让我们马上开始 Transformable 大冒险吧!
Let’s go!!!😉
1. 查无此“类”:奇怪的 Transformable 类型
Core Data 是由 Apple 开发的自家数据持久化框架,其历史可以追溯至 2005 年。它最初作为 OS X 10.4(Tiger) 的一部分发布,旨在为 macOS 应用程序提供高效的 对象图 管理和 持久化 支持。
而随着时代的演进,Core Data 的核心设计理念已包括对象关系映射、内存数据管理以及与 SQLite 的无缝集成,但其定位并非单纯的数据库框架,而更像是一个更高层次的模型层解决方案。
6 年之后,Core Data 于 2011 年正式登陆 iOS 5,成为 iOS 开发的标准数据管理工具。这标志着 Core Data 开始支持移动端开发,小码农们能够通过其可视化建模工具和自动化数据同步功能,简化复杂的数据操作流程。
更多关于 CoreData 开发的进一步介绍,请小伙伴们移步如下链接观赏精彩的内容:
- “一人得道,雨燕升天”:Swift 协议扩展助力 CoreData 托管类型(上)
- 一场陟遐自迩的 SwiftUI + CoreData 性能优化之旅(上)
- 消失的它:揭开 CoreData 托管对象神秘的消失之谜(上)
- “群芳争艳”:CoreData 4 种方法计算最大值的效率比较(下)
- 蹊跷的崩溃:CoreData 数据保存时提示“不可接受类型”(Unacceptable type)
- 漫谈初学者处理 CoreData 数据之启示录
- 『第十一章』数据持久化:CoreData 与 CloudKit
众所周知,在 Xcode 中我们可以利用 CoreData 模型编辑器为我们心仪的 App 搭建怡人的数据模型。我们可以按需为模型中表的各个字段选择合适的类型:
在众多耳熟能详的数据模型中,有一个看起来是那么的“恢诡谲怪”,让人一下子摸不着头脑,它就是 Transformable
类型。
和 CoreData 类似的是苹果最新的 SwiftData 框架,它是一款现代化的数据库框架,其采用描述性的设计语言,借助于大量的宏定义,作为一名“单枪匹马”的勇士可以完全脱离于数据库编辑器工具而独立遨游于数据海洋之上。
更多关于 SwiftData 的详细介绍,请小伙伴们移步如下链接观赏精彩的内容:
- 如何在 SwiftUI 视图中使用分页(Paging)机制显示 SwiftData 数据(一)
- 如何让 SwiftUI 中 @Query 宏能够动态改变获取的 SwiftData 数据结果
- 使用 SwiftData 历史记录追踪(History Trace)跨进程同步 App 和 Widgets 间的数据更改
- SwiftData 共享数据库在 App 中的改变无法被 Widgets 感知的原因和解决
- iOS 18 中全新 SwiftData 重装升级,其中一个功能保证你们“爱不释手”
- 『第十二章』数据持久化:SwiftData
我们知道,用 CoreData 模型编辑器设定的绝大多数类型都可以直接对应到其托管类的定义中去:
extension Record {
@nonobjc public class func fetchRequest() -> NSFetchRequest<Record> {
return NSFetchRequest<Record>(entityName: "Record")
}
@NSManaged public var date: Date?
@NSManaged public var randomNumbers: NSObject?
@NSManaged public var result: Bool
@NSManaged public var usingMagic: NSObject?
@NSManaged public var owner: Human?
}
但是,与此截然不同的是: **Transformable
类型在托管类的定义中根本“无迹可寻”。
如上代码所示,randomNumbers 和 usingMagic 都是 Transformable
类型,而实际上,它们却被映射到了所谓 NSObject? 类型的头上。
那么,这个奇怪的 **Transformable
类型到底有神马用途呢?
2. Transformable 存在的真谛
实际上,Core Data 中 Transformable 类型是一种灵活、定制性很强的数据类型,它允许开发者存储那些无法直接映射到 Core Data 内置基本类型(如 String、Int、Date 等)的自定义对象或复杂数据结构。
它的核心作用是通过 序列化与反序列化 机制,将自定义对象转换为可存储的二进制数据(如 Data 类型),从而将它们持久化到数据库(如 SQLite)之中。
形象的说,Core Data 中 Transformable 类型就好像特种部队中的“剃刀小队”,它们本领高强、颜值出众,专门擅长解决特殊的“定制任务”。
光是这样说,可能大家依旧还是不明所以。所以下面我们就分 4 种应用场景来如数家珍的具体说说 Transformable
类型的“超群绝伦”吧。
3. 派生自 NSObject 的内置类
好戏开始了!我们先用 Transformable 类型最简单的使用场景来为整个冒险拉开序幕,即:Array 和 UIColor 对象的存储。
3.1 数组(Array)
最简单的应用场景是保存值类型的数组(Array) 。
首先,在 Record 托管类中新建一个名为 randomNumbers 的字段,并且将其类型设置为 Transformable:
接着,创建一个帮助方法 makeRandomNumbers() 用来生成随机整数数组:
func makeRandomNumbers() -> [Int] {
var ary = [Int]()
for _ in 0..<3 {
ary.append(Int.random(in: 0...1000))
}
return ary
}
最后,我们可以这样保存 Record 的实例:
func fight() throws {
let context = managedObjectContext!
let record = Record(context: context)
record.date = .now
record.result = .random()
record.owner = self
let numbers = makeRandomNumbers()
record.randomNumbers = numbers as NSObject
try context.save()
}
在上面的代码中,我们将整数数组作为 NSObject 类型的值稳妥的存入到了 randomNumbers 字段中。这样做可以成功的原因在于:[Int] 数组的类型实际上是 Array<Int> 类型,而 Array 类型几乎可以“无痛地”转换为 NSArray 类型,最终 NSArray 又恰巧是一个 NSObject 类型。
同样,我们需要将存放在 randomNumbers 字段中的整数数组读取出来,这也只需一个小小的类型转换即可完成:
LabeledContent("") {
Button("行动") {
try! villain.fight()
}
.font(.title3.bold())
.buttonStyle(.borderedProminent)
}
let numbers = record.randomNumbers as! [Int]
LabeledContent("随机数组") {
HStack {
ForEach(numbers, id: \.self) { num in
Text("\(num) ")
.font(.headline)
}
}
}
运行代码可以发现,我们已经可以轻松的保存和取出 randomNumbers 中正确的内容了:
不过此时此刻,眼尖的小伙伴们可能会瞥见 Xcode 控制台中刺眼的警告:
这又是怎么回事呢?
在下一篇博文中,小伙伴们将继续我们的 Transformable 大冒险,敬请期待吧!
总结
在本篇博文中,我们讨论了 CoreData 中 Transformable 到底是什么类型?以及它存在的意义。随后,我们还介绍了将整数数组转换为 Transformable 类型的方法。
感谢观赏,我们下一篇再会吧!😎