您的位置:首页 > 移动开发 > IOS开发

iOS开发-CoreData的简单使用

2015-12-28 15:29 507 查看

iOS开发-CoreData的简单使用

什么是CoreData?

CoreData是iOS5之后才出现的一个框架, 它提供了
对象-关系映射(ORM)
的功能, 即能够将OC对象转换成数据, 保存在SQLite数据库文件中, 也能够将保存在数据库中的数据还原成OC对象.

这个过程中, 我们不需要编写任何的sql语句, 这个有点类似于著名的Hibernate持久框架, 不过功能肯定没有Hibernate强大.

CoreData是如何将数据直接插入数据库呢?

CoreData的使用步骤

一.为没有使用CoreData的工程添加CoreData

1.
Command+N
手工创建.xcdatamodeld文件, 这个过程相当于创建了一个数据库的模板(类型)
2.在.xcdatamodeld文件中, 创建一个实例, 并且为其对象指明对象及类型
3.
Command+N
创建这个
实例
实例类
(CoreData->NSManagedObject subclass)
4.在使用coreData控制器的位置, 进行初始化:

1

#import <CoreData/CoreData.h>

1

@property(nonatomic,strong)NSManagedObjectContext * context;

-ViewDidLoad:

12
3
4
5
6
7
8
9
10
1112
13
14
15
16
17
18
19

// 从主Boundle中获取我们刚才创建的那个数据库模型
NSManagedObjectModel * model = [NSManagedObjectModel mergedModelFromBundles:nil];

// 持久化调度器, 相当于管理人员, 他负责解释/分析上下文的语法, 负责上下文与数据库的交互, 所以它的初始化, 需要指定一个数据库模型.
NSPersistentStoreCoordinator * store = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:model];

// 数据库文件的位置
NSString * doc = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0];
// 数据库文件名
NSString * sqlite = [doc stringByAppendingPathComponent:@"company.sqlite"];

// 告诉管理人员, 需要管理的数据库的名字和路径
[store addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:[NSURL fileURLWithPath:sqlite] options:nil error:nil];

// 声明了一个上下文, 负责这个上下文的协调器就是上一步创建的store.
NSManagedObjectContext * context = [[NSManagedObjectContext alloc] init];

// 上下文关联一下数据库, 需要一个持久化对象,
context.persistentStoreCoordinator = store;

画一张图来解释一下上面的过程:



图中过程1表示, 从mainBoundle中, 将我们刚才手动创建的.xcdatamodeld文件,
反归档
到一个对象(model)中, 并且使用这个模型作为初始化条件(模板), 实例化一个协调器, 那么, 这个协调器就知道了自己所管理的数据库是什么样的(里面有什么东西).
过程2表示, 协调器根据刚才的模板, 为自己添加一个数据库, 协调器会根据手中的模板, 将这个数据库“改造”成.xcdatamodeld文件描述的样子.
过程3表示, 我们创建一个上下文, 并且将该上下文对象托管给刚才的协调器.
最终, 上下文和数据库通过协调器建立起联系, 我们操作上下文, 达到操作数据库的目的.

CoreData实现实例的增删改查

根据寡人多年的工作经验, 数据库的开发, 你需要掌握的东西其实并不多, 或者说”数据库开发的学习成本非常低”. 别忽略前提, 我是说, 如果你只开发的话.

掌握数据库开发, 无外乎5点. 增删改查+多表关联.

向表中增加一个对象
IBAction响应事件:

12
3
4
5
6
7
8
9
10
1112
13
14
15
16
17
18
19
20
2122
23
24
25

// 数据库准备好了, 开始做基本的操作.
// !!!:添加一个员工
-(IBAction)addEmployee:(id)sender
{
/*
这个对象不能简单的使用 alloc+init 方法创建, 因为这样创建的对象,
和我们的上下文没有关系. 使用NSEntityDescription方法,
可以从当前上下文所指向的 协调器 对应的 数据库模型中,
拿出这个类. 在这个过程中, 我们使用到了_context,
进而将这个对象和我们的_context建立了联系.*/

// 创建一个员工对象
Employee * emp = [NSEntityDescription insertNewObjectForEntityForName:@"Employee" inManagedObjectContext:_context];

emp.name = @"强哥";
emp.height = @1.75;
emp.birthday = [NSDate date];

// 调用上下文的保存方法.
NSError * error ;
[_context save:&error];
if (error) {
NSLog(@"%@",error);
}
}

通过上面的示例我们可以看到:

1.整个操作过程我们没有任何使用sql语句的地方. 这就是封装的好处之一.

2.想要入库的对象, 必须通过NSEntityDescription返回对象类型.

如果用图表示的话, 应该是这样



从表中查询数据.
IBAction响应事件:

12
3
4
5
6
7
8
9
10
1112
13
14
15
16
17
18
19
20
2122
23
24
25

// !!!:查员工
// 在查询数据时, 你需要创建一个请求的对象, 这个对象你可以把它理解成一份申请表, 在上面填写你需要的数据, 过滤条件, 排序规则等约束条件. 然后把这个申请表交给上下文去执行.
-(IBAction)selectALlEmp:(id)sender
{
// 创建请求清单. (向清单上填写, 你需要查询那张表的数据?)
NSFetchRequest * request = [NSFetchRequest fetchRequestWithEntityName:@"Employee"];

// 设置请求条件, 谓词匹配. (在清单上继续写, 需要什么过滤条件不?)
NSPredicate * pre = [NSPredicate predicateWithFormat:@"name = %@", @"强哥"];
request.predicate = pre;

// 设置排序 (继续清单上写, 查询好的数据, 用不用/按照哪个字段排序?)
request.sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:@"height" ascending:YES]];

//// 上面的清单都填好了吧? 那么就将这个request清单, 交给_context上下文去执行吧!
// 执行, 结果给你返回一个字典, 元素为这个表对应的实例类的对象.
NSError * error = nil;
NSArray * array = [_context executeFetchRequest:request error:&error];
if (error) {
NSLog(@"%@",error);
return;
}

NSLog(@"%@",array);
}

这个过程是不是比较繁琐?

试试Xcode自带的代码块:fetch, 上面的方法中, 输入这个单词试试!~

从表中修改数据.
IBAction响应事件:

12
3
4
5
6
7
8
9
10
1112
13
14
15
16
17
18
19
20
2122
23
24
25
26
27
28
29
30

// !!!:修改员工信息
// 修改信息和删除信息, 都需要先通过上下文查找到需要操作的数据, 然后再对其修改或删除
-(IBAction)updateEmp:(id)sender
{
// 填写一个查询清单, 想要修改数据先得查询出来.
NSFetchRequest * request = [NSFetchRequest fetchRequestWithEntityName:@"Employee"];
// 设置请求条件
NSPredicate * pre = [NSPredicate predicateWithFormat:@"name = %@", @"强哥"];
request.predicate = pre;
// 执行, 返回结果
NSError * error = nil;
NSArray * array = [_context executeFetchRequest:request error:&error];
if (error) {
NSLog(@"%@",error);
return;
}

//// 以上动作是为了先找出需要修改的数据, 这些数据存到了一个数组array中,
// 注意, 这个数组是上下文中的数组, 因为, 它是由上下文查询后得到的.

// 更改
for (Employee * emp in array) {
if ([emp.name isEqualToString:@"强哥"]) {
emp.height = @2.0;
}
}

// 修改之后, 保存当前上下文.
[_context save:nil];
}

从表中删除数据.
IBAction响应事件:

12
3
4
5
6
7
8
9
10
1112
13
14
15
16
17
18
19
20
2122
23
24
25
26
27

// !!!: 删除
-(IBAction)delEmp:(id)sender
{
// 填写一个查询清单, 想要删除数据先得查询出来.
NSFetchRequest * request = [NSFetchRequest fetchRequestWithEntityName:@"Employee"];
// 设置请求条件
NSPredicate * pre = [NSPredicate predicateWithFormat:@"name = %@", @"强哥"];
request.predicate = pre;
// 执行, 返回结果
NSError * error = nil;
NSArray * array = [_context executeFetchRequest:request error:&error];
if (error) {
NSLog(@"%@",error);
return;
}

// 删除某人
for (Employee * emp  in array) {
if ([emp.name isEqualToString:@"强哥"]) {
// 这里注意, 不是删除array中的数据, 是从上下文中将这个对象删除.
[_context deleteObject:emp];
}
}

// 保存
[_context save:nil];
}



CoreData的增删改查我们已经完成了, 接下来我们回到sqlite的世界中, 来看看用sql语句如何实增删改查.下面的例子是非常简单的例子, 但是却是数据库开发的基本语法元素.

简单DDL语法:

增: insert into TableName ([colum1, colum2, colum3]) values ([张三, 28, 男]);

删: delete from TableName where name = “张三”;

改: update TableName set name = “李四”;

查: select name from TableName where name = “李四”;

这些基本的语法加上表关联(jion), 能达到的效果就不一般了, 假设我们有两张表: tableA 和 tableB

12
3
4
5
6
7
8
9
10

-- 您能猜出我用的是什么数据库语言吗?
select
A.name,
A.grade,
B.mathScore
from
tableA A
left join tableB B on A.id = B.id
where
A.gender = '男'

上面这段代码, 大概描述了 两张表关联使用, 从A表中取得学生的名字, 从B表中获得了学生的年级和数学成绩.

这样简单的代码您可能在大学的时候学习过, 但是请不要小觑它, 这种select格式能完成的功能远远超乎您的想象.

我这里没有使用场景, 无法为您演示一些复杂的示例.

那么对于CoreData, 我们不用直接接触sql语句, 这种表间的联合查询我们应该怎么办呢?

CoreData 的联合查询.
1.我们创建一个
部门
的示例, 请注意 Employee 的 Releationships 部分.





这里, 实际上Department做为Employee的外键, 在Employee中有一个字段为depart. 如此设置之后,这两张表已经完成前面我们描述的
表间关联
, 不用出现join关键字, 我们已经将两张表牢牢的绑在一起了.

在上代码之前, 我们还需要重新生成以下 Employee和Department的 实例类文件.

CoreData -> NSManagedObject subclass



没得选, 只有这一个.



全部勾选上, 为这两个实例产生两组实例类.



生成之后, 他们从boundle中看起来是这样子的.



下面, 我们向这两个实例中, 分别插入两条数据, 注意:如果你使用的是刚才的工程, 请先删除Doucment中数据库文件, 然后运行demo重新生成一个.

12
3
4
5
6
7
8
9
10
1112
13
14
15
16
17
18
19
20
2122
23
24
25
26
27
28

-(IBAction)addDepartEmp:(id)sender
{
// 创建两个部门, ios 和 安卓
Department * depiOS = [NSEntityDescription insertNewObjectForEntityForName:@"Department" inManagedObjectContext:_context];
depiOS.departmentName = @"iOS部门";
depiOS.departmentID = @1;
depiOS.createDate = [NSDate date];

Department * depAndroid = [NSEntityDescription insertNewObjectForEntityForName:@"Department" inManagedObjectContext:_context];
depAndroid.departmentName = @"安卓部门";
depAndroid.departmentID = @2;
depAndroid.createDate = [NSDate date];

// 创建两个员工, 强哥->iOS部门, 丰哥->安卓部门
Employee * emp1 =[NSEntityDescription insertNewObjectForEntityForName:@"Employee" inManagedObjectContext:_context];
emp1.name = @"强哥";
emp1.height = @1.75;
emp1.birthday = [NSDate date];
emp1.depart = depiOS;

Employee * emp2 =[NSEntityDescription insertNewObjectForEntityForName:@"Employee" inManagedObjectContext:_context];
emp2.name = @"丰哥";
emp2.height = @2.75;
emp1.depart = depAndroid;
emp2.birthday = [NSDate date];

[_context save:nil];
}

插入数据之后, 我们再写一个新的查询方法, 这次我只查询 Employee, 那么看看 这个Employee对象中, 是否包含部门的信息?

12
3
4
5
6
7
8
9
10
1112
13
14
15
16
17
18
19
20
2122

-(IBAction)selectAllEmpDepart:(id)sender
{
// 找到强哥, 获取请求对象, 创建时, 你需要指定要查询那张表.
NSFetchRequest * request = [NSFetchRequest fetchRequestWithEntityName:@"Employee"];

// 设置请求条件, 谓词匹配
NSPredicate * pre = [NSPredicate predicateWithFormat:@"name = %@", @"强哥"];
request.predicate = pre;

// 执行, 返回结果
NSError * error = nil;
NSArray * array = [_context executeFetchRequest:request error:&error];
if (error) {
NSLog(@"%@",error);
return;
}

// 注意, 这里我只查询了员工(Employee), 但是部门信息也顺带查出来了.
for (Employee * emp in array) {
NSLog(@"%@->%@", emp.name, emp.depart.departmentName);
}
}

结果:

1

强哥->iOS部门

所以! CoreData可以通过创建表外键的方式, 将表关联起来. 但是作为Sql开发多年的老人来说, 这种方非常笨重.

二.新建带有CoreData的工程

创建工程时, 我们勾选下面的
Use Core Data
.



在主Boundel中, 最明显的就是我们创建了数据库模板(.xcdatamodeld文件)



打开Appdelegate.h文件, 多了如下属性和方法:
12
3
4
5
6

@property (readonly, strong, nonatomic) NSManagedObjectContext *managedObjectContext;
@property (readonly, strong, nonatomic) NSManagedObjectModel *managedObjectModel;
@property (readonly, strong, nonatomic) NSPersistentStoreCoordinator *persistentStoreCoordinator;

- (void)saveContext;
- (NSURL *)applicationDocumentsDirectory;

在appdelegate.m中, 多了如下的代码 — 我的天啊!




千万别害怕, 静下心来好好看看, 这些方法和属性, 对比我们最开始ViewDidLoad里面的过程, 仔细读一遍, 看看它们再干什么呢? 提示: 懒加载.

最后, 我们来比对一下Sqlite和CoreData的优劣和不同的使用场景.

名称优点缺点
sqlite灵活, 非常灵活当项目中, 模型Model多起来后, 非常不方便, 需要大量代码重写Model的访问和改写等
CoreData面对大量模型时, 非常方便的创建了所有实例类但是一旦这些模型之间的关系较为复杂时, 它的优雅便成了臃肿
最后总结一下, 这两种数据持久化方案都有各自的使用场景:

sqlite 适合数据模型(Model类)稀少, 不需要大量的Model类入库, 但是这些模型之间, 有这复杂的联系, 表间满足各种范式规范, 比如金融类APP, 其中数据需要在手机端完成运算和展示时, 使用sqlite 会非常的方便.

CoreData 适合使用大量Model 的APP, 这些Model之间联系不是那么紧密. 每个模型都是单独拿出来用的, 那么CoreData为你生成的模型访问方法会让你信心百倍!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: