(一)CoreData - 基本使用
@(HTML5秘籍)[Objective-c, iOS, 本地存储]
题目虽然说的是快速入门,但是
CoreData
是一门很博大精深的技术,还是不要妄想几天之内能融会贯通。接下来几篇文章会由浅入深,逐步讲解如何使用并掌握CoreData
。
最近一直在找一些关于日程管理的软件,有一些感觉还不错的,但是总觉得自己想要一些功能都没有,所以干脆就下手自己写一个自己的。由于没有后台服务器做接口,一些数据只能暂时做本地存储。在技术选型的时候做了一些调查,因为之前做过PHP,对SQL印象蛮好的。但是对于一个移动端应用来说,基本不需要大量的存储数据,因此在FMDB,Realm和CoreData之间,选择了苹果的亲儿子CoreData。
[TOC]
封装CoreData管理工具
1. 创建一个自带CoreData代码的工程
在Xcode创建工程的时候,就可以创建一个带有CoreData的工程,在APPdelegate中会生成关于CoreData的代码。
下图是Xcode7自动创建的CoreData代码,Xcode 8 做了修改,我们先说一下旧版的怎么玩,然后再看看新版怎么弄。
虽然说已经生成了这些代码,但是还是不能直接用,我们需要将代码重新封装一下。
2. 简单封装CoreData管理类
① 创建一个继承于NSObject的类 LTCoreDataManager
② 写一个单例作为初始化方法
+ (LTCoreDataManager *)shareLTCoreDataManager
{
static LTCoreDataManager *manager = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
manager = [[LTCoreDataManager alloc] init];
});
return manager;
}
③ 将自动生成的代码粘贴到 LTCoreDataManager.m 中
④ 在 LTCoreDataManager.h 中加入方法声明
+ (LTCoreDataManager *)shareLTCoreDataManager;
@property (readonly, strong, nonatomic) NSManagedObjectContext *managedObjectContext;
@property (readonly, strong, nonatomic) NSManagedObjectModel *managedObjectModel;
@property (readonly, strong, nonatomic) NSPersistentStoreCoordinator *persistentStoreCoordinator;
- (void)saveContext;
- (NSURL *)applicationDocumentsDirectory;
⑤ 注意:应在 LTCoreDataManager.h 中引入 <CoreData/CoreData.h>
创建CoreData模型文件
1.构建模型文件
在创建工程的时候,如果选择了使用CoreData
,Xcode会自动生成一个模型文件,模型文件的后缀为.xcdatamodeld
。如果没有选择自动生成模型文件,我们可以手动创建一个模型文件Command + N,选择 Core Data -> Data Model -> Next
。
左侧有三个选项,entities
、fetch requests
、 configurations
。
2.创建实体
长按左下方的 Add Entity 按钮,会弹出菜Add Entity
、Add Fetch Request
、Add Configuration
。选择 Add Entity
创建一个实体,命名为Plan
。如下图所示
右侧对应着Attributes
(属性)、Relationships
(关联关系)、Fetched Properties
(获取操作)。
首先,添加两个属性,planName
:type 为 String,planId
:type 为 Integer64。
需要注意的是,属性的首字母要小写。
关于Type类型的说明
- Undefined: 默认值,参与编译会报错
- Integer 16: 整数,表示范围 -32768 ~ 32767
- Integer 32: 整数,表示范围 -2147483648 ~ 2147483647
- Integer 64: 整数,表示范围 –9223372036854775808 ~ 9223372036854775807
- Float: 小数,通过MAXFLOAT宏定义来看,最大值用科学计数法表示是 0x1.fffffep+127f
- Double: 小数,小数位比Float更精确,表示范围更大
- String: 字符串,用NSString表示
- Boolean: 布尔值,用NSNumber表示
- Date: 时间,用NSDate表示
- Binary Data: 二进制,用NSData表示
- Transformable: OC对象,用id表示。可以在创建托管对象类文件后,手动改为对应的OC类名。使用的前提是,这个OC对象必须遵守并实现NSCoding协议。
3.添加关联关系
再创建一个实体Task
并添加响应的属性。
给Task
添加关联关系,点击Relationships
下面的加号,新建一个关联关系,命名为plan
,inverse
需要设置好Relationships
之后才能设置。
关联关系设置
- delete rule: 定义关联属性的删除规则。在当前对象和其他对象有关联关系时,当前对象被删除后与之关联对象的反应。这个参数有四个枚举值,代码对应着模型文件的相同选项。
- NSNoActionDeleteRule 删除后没有任何操作,也不会将关联对象的关联属性指向nil。删除后使用关联对象的关联属性,可能会导致其他问题。
- NSNullifyDeleteRule 删除后会将关联对象的关联属性指向nil,这是默认值。
- NSCascadeDeleteRule 删除当前对象后,会将与之关联的对象也一并删除。
- NSDenyDeleteRule 在删除当前对象时,如果当前对象还指向其他关联对象,则当前对象不能被删除。
- Type: 主要有两种类型,To One和To Many,表示当前关系是一对多还是一对一。
4.创建托管对象类文件
创建文件
选中后缀名为.xcdatamodeld
的模型文件,选择Xcode
的Editor
-> Create NSManagedObject Subclass
-> 选择模型文件
-> 选择实体
,生成实体对应的托管对象类文件。
更新文件
当前模型对应的实体发生改变后,需要重新生成模型文件。生成步骤和上面一样,主要是替换Category文件,托管对象文件不会被替换。生成文件时不需要删除,直接替换文件。
增删改查操作
1.插入操作
// 创建托管对象,并指明创建的托管对象所属实体名
Plan *planObj = [NSEntityDescription insertNewObjectForEntityForName:@"Plan" inManagedObjectContext:context];
planObj.planName = @"计划名字";
planObj.planId = [NSNumber numberWithInteger:1];
// 通过上下文保存对象,并在保存前判断是否有更改
NSError *error = nil;
if (context.hasChanges) {
[context save:&error];
}
// 错误处理
if (error) {
NSLog(@"CoreData Insert Data Error : %@", error);
}
通过NSEntityDescription
的insert
类方法,生成并返回一个Employee
托管对象,并将这个对象插入到指定的上下文中。
managedObjectContext
将操作的数据存放在缓存层,只有调用managedObjectContext
的save
方法后,才会真正对数据库进行操作,否则这个对象只是存在内存中,这样做避免了频繁的数据库访问。
2.删除操作
// 建立获取数据的请求对象,指明对Plan实体进行删除操作
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"Plan"];
// 创建谓词对象,过滤出符合要求的对象,也就是要删除的对象
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"planName = %@", @"计划名字"];
[request setPredicate:predicate];
// 执行获取操作,找到要删除的对象
NSError *error = nil;
NSArray<Plan *> *planArr = [context executeFetchRequest:request error:&error];
// 遍历符合删除要求的对象数组,执行删除操作
[planArr enumerateObjectsUsingBlock:^(Plan * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
[context deleteObject:obj];
}];
// 保存上下文
if (context.hasChanges) {
[context save:nil];
}
// 错误处理
if (error) {
NSLog(@"CoreData Delete Data Error : %@", error);
}
3.更新操作
// 建立获取数据的请求对象,并指明操作的实体为Plan
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"Plan"];
// 创建谓词对象,设置过滤条件
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"planName = %@", @"计划名字"];
request.predicate = predicate;
// 执行获取请求,获取到符合要求的托管对象
NSError *error = nil;
NSArray<Plan *> *planArr = [context executeFetchRequest:request error:&error];
[planArr enumerateObjectsUsingBlock:^(Plan * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
obj.planId = @2;
}];
// 将上面的修改进行存储
if (context.hasChanges) {
[context save:nil];
}
// 错误处理
if (error) {
NSLog(@"CoreData Update Data Error : %@", error);
}
4.查询操作
// 建立获取数据的请求对象,指明操作的实体为Plan
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"Plan"];
// 执行获取操作,获取所有Plan托管对象
NSError *error = nil;
NSArray<Plan *> *planArr = [context executeFetchRequest:request error:&error];
[planArr enumerateObjectsUsingBlock:^(Plan * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
NSLog(@"Plan Name : %@, Id : %@", obj.planName, obj.planId);
}];
// 错误处理
if (error) {
NSLog(@"CoreData Ergodic Data Error : %@", error);
}
小结一下
看完上面一大堆,其实应该还是不太理解。不过跟着步骤一步步走下来,应该是可以简单实用了。