请写一篇关于【Core Data入门:iOS数据持久化方案】的技术文章
作者:光谷码农小李,白天写Swift,晚上刷行测,目标是明年上岸湖北省考
上周五晚上10点半,我瘫在光谷软件园B2栋楼下那把被无数加班狗坐得发亮的长椅上,一边啃着热干面,一边用手机刷“湖北人事考试网”。老婆刚发来微信:“复习得怎么样了?明天模考别又睡过头。”我苦笑了一下,回了个“OK”表情包。
其实我连《申论》材料都没看完——因为白天公司新需求上线,我卡在了一个该死的本地数据存储问题上。老板说:“这个功能下周三必须上线,用户收藏的数据得持久化啊!”我心想:行吧,那就再熬个通宵搞定 Core Data 吧。
毕竟,一个想考公的程序员,不能倒在自己的技术栈上。
一、从 JavaScript 转 iOS 开发?别笑,真有人这么干
说出来你可能不信,我大学学的是前端,主攻 Vue + Node.js,工作第一份 offer 是做 Web 应用,月薪15k,房租3500(武汉关山大道合租)。但疫情那年,公司裁员,我被迫转型移动端。
起初我以为 iOS 开发就是“换个语法写 JS”,结果第一次接触 Core Data 时,直接懵了:什么 NSManagedObjectContext?NSManagedObjectModel?这命名风格比 React 的 hooks 还让人头大!
我翻遍了中文教程,很多要么照搬 Apple 官方文档(全是英文术语堆砌),要么就是“三行代码搞定存储”(实际跑起来 Crash 到怀疑人生)。最气人的是,有些所谓“实战教程”居然用 Playground 演示——可谁在真实项目里用 Playground 做持久化啊?
于是,我决定自己动手,边踩坑边总结。今天这篇,就是我熬了三个周末+两个通宵后的真实心得。不讲理论套话,只讲怎么让代码跑起来、数据存得住、App 不闪退。
二、为什么不用 SQLite 或 UserDefaults?Core Data 真香警告!
很多人一听到“数据持久化”,第一反应是:“直接用 SQLite 不就完了?”或者更懒一点:“UserDefaults 存个数组不行吗?”
我试过。真的不行。
去年十月,我负责重构公司一个新闻类 App 的“收藏夹”功能。最初用 UserDefaults 存 [String],结果用户收藏超过200条后,App 启动慢得像加载90年代网页。测试同事直接在群里@我:“小李,你这收藏功能点开要等8秒,用户早卸载了!”
后来改用 FMDB(SQLite 封装库),虽然快了,但每次增删改都要手动写 SQL,还容易出错。比如删除某条记录后忘记清理关联图片缓存,导致磁盘爆满——运维大哥差点把我从工位上拎出去。
这时候,老王(我们组 iOS 架构师,45岁,两个娃,但头发还在)拍了拍我肩膀:“试试 Core Data 吧,苹果亲儿子,自动内存管理,关系映射也方便。”
我半信半疑:“可网上都说 Core Data 复杂、坑多……”
他笑了:“复杂是因为你没用对工具。它不是数据库,是对象图管理器。你把它当 ORM 用,自然顺手。”
这句话,成了我的转折点。
##三、实战:用 Core Data 实现“收藏夹”功能(附完整流程)
下面是我上周五真正上线的代码逻辑,已脱敏,可直接复用。
步骤1:创建数据模型(.xcdatamodeld 文件)
在 Xcode 里右键 → New File → Core Data → Data Model。
新建一个 Entity 叫 Bookmark,添加属性:
title: Stringurl: StringcreatedAt: Date
⚠️ 注意:不要勾选 “Codegen: Class Definition”!否则你没法自定义类。选 “Manual/None”,然后手动创建 Bookmark+CoreDataClass.swift 和 Bookmark+CoreDataProperties.swift。
步骤2:初始化 Core Data Stack(关键!)
很多人在这里栽跟头。我建议把 Core Data 初始化封装成单例:
import CoreData
class CoreDataManager {
static let shared = CoreDataManager()
lazy var persistentContainer: NSPersistentContainer = {
let container = NSPersistentContainer(name: "YourModelName")
container.loadPersistentStores { _, error in
if let error = error {
fatalError("Core Data load failed: \(error)")
}
}
return container
}()
var viewContext: NSManagedObjectContext {
return persistentContainer.viewContext
}
}
💡 提醒:viewContext 仅用于主线程读写!如果要做后台导入(比如从网络批量拉收藏),必须用
performBackgroundTask。
步骤3:保存一条收藏
func saveBookmark(title: String, url: String) {
let context = CoreDataManager.shared.viewContext
let bookmark = Bookmark(context: context)
bookmark.title = title
bookmark.url = url
bookmark.createdAt = Date()
do {
try context.save()
print("✅ 收藏成功")
} catch {
print("❌ 保存失败: \(error)")
}
}
步骤4:查询所有收藏(带排序)
func fetchAllBookmarks() -> [Bookmark] {
let request: NSFetchRequest<Bookmark> = Bookmark.fetchRequest()
request.sortDescriptors = [NSSortDescriptor(key: "createdAt", ascending: false)]
do {
return try CoreDataManager.shared.viewContext.fetch(request)
} catch {
print("❌ 查询失败: \(error)")
return []
}
}
步骤5:删除(千万记得先 delete 再 save!)
func deleteBookmark(_ bookmark: Bookmark) {
let context = CoreDataManager.shared.viewContext
context.delete(bookmark)
do {
try context.save()
} catch {
print("❌ 删除失败: \(error)")
}
}
四、那些让我半夜惊醒的坑(以及怎么填)
坑1:多线程访问崩溃
有次我在网络回调里直接往 viewContext 插数据,结果 App 随机闪退。日志显示:“Core Data concurrency violation”。
解决方法:所有后台操作必须用私有上下文。
CoreDataManager.shared.persistentContainer.performBackgroundTask { backgroundContext in
let bookmark = Bookmark(context: backgroundContext)
// ...赋值
do {
try backgroundContext.save() // 这里 save 只影响后台上下文
// 如果需要刷新 UI,发通知到主线程
DispatchQueue.main.async {
NotificationCenter.default.post(name: .dataUpdated, object: nil)
}
} catch { /* handle */ }
}
坑2:模型升级(Migration)搞崩线上用户
我们有一次加了个 isRead: Bool 字段,没做轻量级迁移,结果老用户一打开 App 就白屏。
后来学会:只要改了 .xcdatamodeld,就必须创建新版本!
Xcode 里选中模型文件 → Editor → Add Model Version。然后设置新版本为 “Current”。
对于简单字段变更(比如新增非空字段),勾选 “Lightweight Migration” 即可自动处理。
坑3:调试时看不到数据?
用 Core Data Lab(付费工具,$29)或免费的 SQLPro for SQLite 打开模拟器里的 .sqlite 文件。路径一般在:
~/Library/Developer/CoreSimulator/Devices/.../data/Containers/Data/Application/.../Documents/
看到真实数据那一刻,我感动得差点给 Apple 跪下。
五、为什么一个考公的人还在死磕技术?
可能你会问:你不是要考公吗?干嘛还花时间研究 Core Data?
说实话,我也纠结过。去年底报名省考岗位时,老婆问我:“你确定要放弃现在22k的月薪,去拿5k的公务员工资?”
我说:“不是放弃,是选择另一种稳定。但在上岸前,我得对得起这份工作,对得起自己写的每一行代码。”
技术是我的饭碗,也是我的底气。哪怕明天就去政务大厅窗口上班,今天我也要把这个收藏功能做到零 Bug。
而且你知道吗?备考和写代码其实很像:
- 行测 = 算法题,讲究速度与准确率;
- 申论 = 代码注释,要逻辑清晰、观点鲜明;
- 面试 = Code Review,考察你的表达与思维。
所以,我不觉得考公和写 Core Data 冲突。相反,越是焦虑的时候,越要把手头的事做到极致——因为掌控感,是唯一能对抗不确定性的武器。
六、给同样在挣扎的你:几点真心建议
- 别迷信“简单方案”:UserDefaults 存小配置可以,但别硬扛复杂数据。Core Data 虽重,但苹果生态里它是最稳的。
- 善用工具:除了上面提到的 Core Data Lab,还可以用 iOS Dev Tools 网站查 API,用 SwiftLint 规范代码。
- 教程要挑“带项目”的:推荐 Ray Wenderlich 的 Core Data 教程(英文但超详细),或者 Bilibili 上“即刻iOS”系列(有中文实战)。
- 允许自己慢一点:我花了一周才搞懂 Context 层级。没关系,程序员的成长从来不是线性的。
结语:在光谷的夜里,代码和梦想都值得被保存
昨天模考,我申论写了“数字政府建设中的数据安全与共享”,突然笑出声——这不就是我在做的事儿吗?只不过,我把“公民信息”换成了“用户收藏”,把“政务云”换成了 iCloud Sync。
也许明年此时,我已经坐在某区人社局的办公室里,敲着 Excel 而不是 Xcode。但此刻,在光谷软件园这盏凌晨一点的灯下,我依然认真地为每一个 NSManagedObjectContext 加上 do-catch,为每一次 save() 做错误兜底。
因为我知道:无论是考公还是写代码,底层逻辑都是同一件事——把重要的东西,好好存下来。
共勉。
作者备注:本文所有代码已在真实项目上线,无任何虚构。如果你也在武汉备考+打工,欢迎私信交流(但别问行测技巧,我资料分析还在错一半 😅)。

评论 0