当前位置: 首页 > news >正文

【iOS】Core Data

【iOS】Core Data

  • 前言
  • Core Data基本概念
  • Core Data核心组件
  • Core Data增删改查
  • 总结

前言

笔者写项目时用到了Core Data,在此系统学习总结一下。

Core Data基本概念

Core Data是Apple官方推出,适配iOS/macOS的本地数据持久化与对象图管理框架,并非数据库,而是封装了SQLite、二进制、XML存储的ORM框架。开发者无需编写SQL语句,可直接通过操作OC对象实现数据增删改查,同时内置缓存、事务、撤销重做、数据版本迁移能力。

其优势主要在于:

  • 简化数据管理:提供ORM映射自动完成OC对象与数据库数据的转换,减少了直接与SQL数据库的交互。
  • 高效数据查询:通过NSPredicate进行复杂的数据查询,提高数据检索的灵活性和效率。
  • 内置缓存机制:上下文自动缓存已加载数据,减少频繁磁盘IO,提升数据读写性能。
  • 版本控制:自动处理数据模型的版本管理,支持数据迁移。

Core Data采用分层栈架构,所有数据操作遵循Model->Context->Coordinator->持久化文件的流转逻辑。

其核心组件包括:

  • NSManagedObjectModel(数据模型):等同于数据库Schema,定义Entity(数据表)、Attribute(字段)、Relationship(表关联),是整个数据体系的结构基础。

Schema:数据库中表、视图、索引、存储过程等数据库对象的集合,相当于数据库内部的命名空间。

  • NSManagedObjectContext(托管上下文/MOC):数据操作的核心工作区,负责追踪数据变更、管理托管对象生命周期、执行数据的增删改查、提交保存事务。
  • NSPersistentStoreCoordinator(持久化协调器/PSC):中间调度层,衔接上下文与底层存储,屏蔽不同存储类型(SQLite/二进制/XML)的差异,开发者无需直接操作。
  • NSManagedObject(托管对象):数据实体实例,对应数据库中的单条数据记录,通过KVC完成字段赋值与取值。

NSPersistentContainer:iOS10+推出的整合容器,一键封装核心组件,简化Core Data初始化配置。

Core Data核心组件

  1. NSManagedObjectModel

数据模型对应数据库Schema,无任何业务逻辑,只负责定义数据结构与关联关系。

模型的核心结构有:

  • Entity:实体。对应数据库数据表。
  • Attribute:属性、字段。实体的具体字段,支持各种数据类型。
  • Relationship:关联关系。实体与实体之间的关联,支持一对一、一对多、多对多,对应数据库外键关联。
  • Fetched Property:查询属性。预定义查询规则,无需重复写查询条件,提升复用性。

数据模型有两种创建方式:

  • .xcdatamodeld文件:Xcode手动创建Data Model文件,编译后自动生成.momd文件,代码读取该文件加载模型。

  • 纯代码动态构建:适合解决多人协作git文件合并出现冲突问题。

我自己和别人协作的MemoryCity的项目里就使用了纯代码构建的方式,这样不依赖NSManagedObject子类,不生成任何托管对象子类,后续所有读写操作数据统一使用KVC。

-(NSManagedObjectModel*)managedObjectModel{// 初始化模型对象NSManagedObjectModel*model=[[NSManagedObjectModel alloc]init];// 初始化Entity实体NSEntityDescription*cachedPostEntity=[[NSEntityDescription alloc]init];cachedPostEntity.name=kYALCachedPostEntityName;cachedPostEntity.managedObjectClassName=@"NSManagedObject";// 创建Attribute字段NSMutableArray<NSAttributeDescription*>*attributes=[NSMutableArray array];[attributes addObject:[selfstringAttributeWithName:@"cacheScope"]];// ...添加多个属性// 绑定字段到实体cachedPostEntity.properties=attributes;// 将实体加入模型model.entities=@[cachedPostEntity];returnmodel;}
  1. NSManagedObjectContext

MOC是Core Data中管理托管对象生命周期和数据库持久化的核心组件。所有数据的增删改查都必须依托上下文完成,即内存中的数据草稿区。数据变更现在内存中生效,调用save:后才真正持久化到磁盘。

MOC核心特性:

  • 事务支持:MOC支持事务操作,可以确保数据的一致性和完整性,开发者可以使用它执行批量的数据插入、更新等操作。
  • 缓存机制 :MOC提供了一个内存中的对象缓存,所有对数据的操作首先在缓存中进行,这样可以避免频繁的磁盘I/O操作,提升性能。
  • 变更追踪 :MOC能够追踪对数据模型所做的更改,并管理相关对象的生命周期,这简化了数据同步和更新的过程。
  • 非线程安全:内部无同步锁保护,严禁跨线程操作,多线程需单独创建子上下文。

获取主线程上下文示例:

@interfaceSceneDelegate()@end@implementationSceneDelegate@synthesizepersistentContainer=_persistentContainer;-(NSPersistentContainer*)persistentContainer{if(_persistentContainer==nil){// 创建Core Data容器并加载数据模型,"ModelName"为.xcdatamodeld文件名字// iOS10+后,NSPersistentContainer内部帮我们创建好model、PSC、MOC_persistentContainer=[[NSPersistentContainer alloc]initWithName:@"ModelName"];// 打开数据库,loadPersistentStores内部为我们完成了:// 1. 找SQLite文件,没有就创建并打开// 2. 创建Persistent Store持久化仓库,连接起来PSC// 3. 建立映射,将定义的Entity存储类型为SQLite,Core Data将每个Entity自动映射为一张Table数据表,所有SQL语句在由Core Data在内部自动生成,持计划仓库读写这份SQLite文件// 4. 完成后回调:p1:表示当前加载的Store描述(如SQLite文件位置、是否只读等等) p2:表示是否加载打开数据库失败[_persistentContainer loadPersistentStoresWithCompletionHandler:^(NSPersistentStoreDescription*storeDescription,NSError*error){if(error!=nil){}}];// 自动合并父子上下文变更_persistentContainer.viewContext.automaticallyMergesChangesFromParent=YES;}return_persistentContainer;}

这里主要通过NSPersistentContainer获取,第一次访问创建并加载,第二次访问直接返回,类似懒加载模式。

其中viewContext在主线程,主要用于界面相关的数据读取和更新。

设置automaticallyMergesChangesFromParent为YES的意义在于:当后台MOC保存数据后,主线程viewContext可以自动合并这些改动,避免界面显示旧数据,可同步前后台上下文数据。

  1. NSPersistentStoreCoordinator

PSC是Core Data底层调度核心,负责衔接上下文与本地持久化文件,屏蔽掉SQLite、二进制和内存等不同存储类型的差异。其支持的存储类型有:

  • NSSQLiteStoreType:默认存储,最常用。基于SQLite数据库,支持负责查询和事务。
  • NSBinaryStoreType:二进制文件存储,适合轻量数据。读写速度快,但不支持复杂查询。
  • NSInMemoryStoreType:内存存储,适合临时缓存数据。数据断电会丢失。
  • NSXMLStoreType:XML文件存储。仅macOS支持,iOS已废弃。

一般情况下,无需手动调用,容器和上下文会自动封装。上面获取主线程上下文的demo里就是通过NSPersistentContainer已经封装好的示例。

手动配置demo:

整个过程其实就是先创建好模型然后关联给PSC,再创建并添加给PSC数据库,最终达到Model、PSC和SQLite关联起来的效果。

// 创建模型NSManagedObjectModel*model=[selfmanagedObjectModel];// 用模型创建PSCNSPersistentStoreCoordinator*coordinator=[[NSPersistentStoreCoordinator alloc]initWithManagedObjectModel:model];// 获取数据库存储路径NSURL*storeURL=[[NSFileManager defaultManager]URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask].lastObject;storeURL=[storeURL URLByAppendingPathComponent:@"CoreDataDemo.sqlite"];// 创建数据库配置// p1:是否允许自动迁移数据,YES表示Core Data会尝试自动升级数据库 p2:是否自动推断MappingModel,YES表示Core Data会自己从旧字段生成为新字段NSDictionary*options=@{NSMigratePersistentStoresAutomaticallyOption:@YES,NSInferMappingModelAutomaticallyOption:@YES};NSError*error=nil;// 给PSC添加数据库if(![coordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:options error:&error]){NSLog(@"PSC配置失败:%@",error.localizedDescription);}
  1. NSManagedObject

NSManagedObject是数据实体的运行实例,对应数据库里单条数据记录,所有操作使用KVC。

常用API:

  • valueForKey::获取字段值
  • setValue:forKey::设置字段值
  • objectID:获取数据唯一标识,可用于精准查询
  • entity:获取当前关联的实体描述

Core Data增删改查

  1. 新增数据

核心步骤为:获取MOC,通过insertNewObjectForEntityForName:inManagedObjectContext:创建实体对象,并使用KVC赋值,最后调用save:持久化数据。

-(void)insertCoreData{NSManagedObjectContext*context=[selfcreateContext];// 创建实体对象NSManagedObject*student=[NSEntityDescription insertNewObjectForEntityForName:@"Student"inManagedObjectContext:context];// 赋值[student setValue:@"lyd"forKey:@"name"];[student setValue:@19forKey:@"age"];// 保存数据NSError*error=nil;if([context save:&error]){NSLog(@"新增数据成功");}else{NSLog(@"新增数据失败:%@",error.localizedDescription);}}

这里会遇到一个问题,大量数据在主线程进行新增保存操作,会使得使得主线程堵塞卡顿。我们使用后台Context在子线程新增保存,最后同步到主线程来解决。

NSManagedObjectContext*context=[[NSManagedObjectContext alloc]initWithConcurrencyType:NSPrivateQueueConcurrencyType];

NSManagedObjectContextConcurrencyType枚举值:

  • NSMainQueueConcurrencyType:主线程上下文。创建后绑定主线程,必须在主线程操作。
  • NSPrivateQueueConcurrencyType:私有队列上下文。拥有独立串行队列,所有操作必须使用performBlock:performBlockAndWait:

Core Data是线程不安全的,Core Data要求所有操作必须跑在MOC自己的队列里,GCD队列无法和Core Data内部事务时序对齐。因此这里必须使用performBlock:performBlockAndWait:

  • performBlock::异步。不阻塞当前线程,适合后台批量写入和异步查询。
  • performBlockAndWait::同步。阻塞调用方,等数据库操作完成后返回。

无论异步或者同步,其本质都是把block提交给上下文自身持有的串行队列执行。

  1. 查询数据

主要基于NSFetchRequest实现,支持条件查询、排序和分页。

-(NSArray*)fetchAllStudent{NSManagedObjectContext*context=[selfmainContext];// 创建查询请求,指定实体NSFetchRequest*request=[[NSFetchRequest alloc]initWithEntityName:@"Student"];// 条件request.predicate=[NSPredicate predicateWithFormat:@"age > %d",age];// 升序request.sortDescriptors=@[[NSSortDescriptor sortDescriptorWithKey:@"age"ascending:YES]];// 分页request.fetchLimit=20;request.fetchOffset=0;NSError*error=nil;NSArray*resultArray=[context executeFetchRequest:request error:&error];if(error){NSLog(@"查询失败:%@",error.localizedDescription);returnnil;}returnresultArray;}
  1. 更新数据

核心逻辑为先查询、再修改、最后保存,修改内存中托管对象属性,保存后同步到磁盘。

-(void)updateStudentData{NSManagedObjectContext*context=[selfmainContext];NSFetchRequest*request=[[NSFetchRequest alloc]initWithEntityName:@"Student"];// NSPredicate:谓词,用来定义过滤条件NSPredicate*predicate=[NSPredicate predicateWithFormat:@"name == %@",@"lyd"];request.predicate=predicate;NSError*error=nil;NSArray*result=[context executeFetchRequest:request error:&error];// 修改匹配到的第一条数据if(result.count>0){NSManagedObject*student=result.firstObject;[student setValue:@20forKey:@"age"];// 保存更新[context save:&error];NSLog(@"数据更新成功");}}
  1. 删除数据
-(void)deleteStudentData{NSManagedObjectContext*context=[selfmainContext];NSFetchRequest*request=[[NSFetchRequest alloc]initWithEntityName:@"Student"];NSPredicate*predicate=[NSPredicate predicateWithFormat:@"age == %d",20];request.predicate=predicate;NSError*error=nil;NSArray*result=[context executeFetchRequest:request error:&error];if(result.count>0){[context deleteObject:result.firstObject];[context save:&error];NSLog(@"数据删除成功");}}

总结

http://www.jsqmd.com/news/1215560/

相关文章:

  • 2026 年当下,新疆比较好的太空舱民宿批发厂家怎么联系,住在宇宙里?这套设计颠覆你的旅行想象 - 企业推荐官【认证官方】
  • drissionpage的websocket._exceptions问题
  • 手机投屏电视实操教程|主流设备适配对比
  • 代码展示大模型的智能
  • 模型推理的输入预处理流水线:CPU 预处理和 GPU 推理解耦的设计与实践
  • HCIA-华为数通基础理论与实践10
  • 2027年7月最新贵阳浪琴官方售后客户服务热线与维修网点地址汇总 - 浪琴官方售后服务中心
  • CSP202409B:字符串 K 次替换查询的高效解法
  • 江阴全域打井哪家靠谱?瑞溪泉水利工程有限公司浅谈江阴岩石深井、降水井与山区打井实战经验及客户好评 - 瑞溪泉水利
  • Cursor自动化脚本性能压测报告:单脚本吞吐量达2143 ops/sec,比传统Shell快11.8倍(附Benchmark原始数据集)
  • F2800157开发板图形化配置 MCAN 报文收发笔记
  • 帝舵中国官方售后服务中心|服务热线及详细地址权威信息通告(2026年7月更新) - 帝舵中国官方服务中心
  • 公司一堆印章借出去就没人管了?我用飞算 JavaAI 做了个印章管控系统
  • 3步掌握WindowResizer:打造完美桌面布局的终极指南
  • 【2026最新】555电影官网入口,稳定一键直达
  • 【Webhook触发器终极指南】:扣子平台98%开发者忽略的5个致命配置陷阱
  • 改稿速度拉满 2026 最新降AI率工具测评与推荐
  • 2026年蜘蛛池最新规则,新手入门必看
  • 几乎跪着刷完飞书的 Agent!真的太清晰了~
  • 新疆旅游报什么团好?2026本地人实测:4种团型优劣+避坑全攻略 - 旅行分享
  • 嘉兴南湖区老厂房改造想打井,前期需要做什么勘察?旧厂房打井勘察要点 - 瑞溪泉水利
  • 亲身到店探访杭州雷达官方售后服务中心|网点地址及24小时电话(2026年7月最新) - 亨得利钟表维修中心
  • AsrTools语音转文字工具:高效批量处理音频视频文件,智能生成SRT字幕
  • 积家香港官方售后服务网点通告:2026年7月最新地址+热线 - 积家官方售后服务中心
  • 安装读卡器驱动WIN10禁用数字签名
  • 1行命令唤醒沉睡的替换能力:Cursor 0.42+ 版本新增`@workspace:replace`语法全解
  • 上海静安黄金回收哪家靠谱?全域103实体门店,双资质检测绝不压低回收价 - 一日一测评
  • 9款AI写论文工具:一键生成开题报告、论文大纲、毕业论文与期刊论文
  • 完整指南:如何在3分钟内安装使用开源视频下载插件
  • LAMP 部署 Discuz 论坛