序言
在了解CoreData,大家有必要了解对象关系映射(英语称object Relational Mapping,简称ORM)。
ORM
ORM是通过使用描述对象和数据库之间映射的元数据,可以实现将对象自动持久化到关系数据库当中。ORM的存在为了解决面向对象与数据库科恩干存在不匹配的一种技术。
一 初识CoreData
1.CoreData是一种在iOS 3系统中,也是苹果自己推出的数据存储框架,采用了一种ORM(对象关系映射)的存储关系。
CoreData一个比较大的优势在于在使用CoreData过程中不需要我们编写SQL语句
,也就是将OC对象存储于数据库,也可以将数据库数据转为OC对象(数据库数据与OC对象相互转换)。
2.CoreData几个类
(1)NSManagedObjectContext
托管对象上下文,数据库的大多数操作是在这个类操作
(2)NSManagedObjectModel
托管对象模型,其中一个托管对象模型关联到一个模型文件,里面存储着数据库的数据结构。
(3)NSPersistentStoreCoordinator
持久化存储协调器,主要负责协调上下文玉存储的区域的关系。
(4)NSManagedObject
托管对象类,其中CoreData里面的托管对象都会继承此类。
三 CoreData基本使用
下面开始讲解CoreData的基本使用,里面会涉及到源码,内容比较多,希望大家静下来看完。
使用CoreData方式,有两种可能。第一种是项目开始就创建带有CoreData数据库,还有一种项目已经开始了,重新接入CoreData,下面我们第三部分主要讲述这两种方式的过程。
1.项目开始就使用CoreData
我们在创建项目的时候,勾选Use Core Data
然后在文件目录中自动生成一个后缀为.xcdatamodeld
的文件
打开AppDelegate
发现类中多了以下内容
- AppDelegate.h
@property (readonly, strong) NSPersistentContainer *persistentContainer;
- (void)saveContext;
- AppDelegate.m
@synthesize persistentContainer = _persistentContainer;
- (NSPersistentContainer *)persistentContainer {
// The persistent container for the application. This implementation creates and returns a container, having loaded the store for the application to it.
@synchronized (self) {
if (_persistentContainer == nil) {
_persistentContainer = [[NSPersistentContainer alloc] initWithName:@"CoreDataDemo"];
[_persistentContainer loadPersistentStoresWithCompletionHandler:^(NSPersistentStoreDescription *storeDescription, NSError *error) {
if (error != nil) {
// Replace this implementation with code to handle the error appropriately.
// abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
/*
Typical reasons for an error here include:
* The parent directory does not exist, cannot be created, or disallows writing.
* The persistent store is not accessible, due to permissions or data protection when the device is locked.
* The device is out of space.
* The store could not be migrated to the current model version.
Check the error message to determine what the actual problem was.
*/
NSLog(@"Unresolved error %@, %@", error, error.userInfo);
abort();
}
}];
}
}
return _persistentContainer;
}
#pragma mark - Core Data Saving support
- (void)saveContext {
NSManagedObjectContext *context = self.persistentContainer.viewContext;
NSError *error = nil;
if ([context hasChanges] && ![context save:&error]) {
NSLog(@"Unresolved error %@, %@", error, error.userInfo);
abort();
}
}
我们可以点开testCoreData.xcdatamodeld文件,我们可以看到实体和关系。如下图
图表说明
-
Add Entity
添加表 -
Add Attrubte
添加属性
创建后可以清楚的看到模型文件左侧的列表,有三个Entities、Fetch Requests以及Configurations三个选项。
-
ENTITIES
实体 -
FETCH REQUESTS
请求模版 -
CONFIGURATIONS
配置信息
添加完一个实体后,你会发现一个实体是对应着三个内容,分别是Attributes、Relationships和Fetched Properties。
-
Attributes
属性 -
Relationships
关联关系 -
Fetched Properties
获取操作
(3)实体属性类型
我们来分别简单解释类型的意义,从上往下
-
Undefined
:也就是默认值,如果参与编译会报错 -
Integer 16
:代表整数,范围是-32768 ~ 32767 -
Integer 32
:代表整数,范围是-2147483648 ~ 2147483647 -
Integer 64
:代表整数,范围是–9223372036854775808 ~ 9223372036854775807,还是很大的,较少用 -
Double
:代表小数 -
Float
:代表小数 -
String
:代表字符串,NSString表示 -
Boolean
:代表布尔值,使用NSNumber表示 -
Date
:代表日期时期 -
Binary Data
:代表二进制,是用NSData表示 -
Transformable
:代表Objective对象,要遵守NSCoding协议
(4)关联关系
点击加号,可以添加关联关系,在inverse这个属性代表两个实体在Relationships设置关联关系后之后,是否可以从一个实体中找到另一个实体,这样使两个实体具有双向的关联关系。
(5)Editor Style
大家通过点击下面红色按钮,style按钮可以看出实体和属性的关系,以及可以看出实体之间的对应的关系。
上面是coreData的视图的基本运用,自己也是一个不断摸索的过程,下面讲述CoreData的基本操作。
三 CoreData基本使用
在讲述操作之前,我们首先讲述NSManagedObjectContext
,苹果推荐使用initWithConcurrencyType
方式创建,在创建时,指定当前是什么类型的并发队列,参数也是一个枚举值。
NSManagedObjectContext枚举值参数有三个类型:
-
NSConfinementConcurrencyType
:此类型在iOS9之后被苹果弃用,所以不建议用这个API。 -
NSPrivateQueueConcurrencyType
:代表私有并发队列的类型,操作也是在子线程中完成的。 -
NSMainQueueConcurrencyType
:代表主并发队列类型,如果在操作过程中,需要涉及到UI操作,则应该使用这个参数初始化上下文完成操作。
创建
NSManagedObjectContext
一共有两种方法,下面分别介绍
1.下面我们以Student
为模型,创建主队列并发类型的NSManagedObjectContext
/** NSManagedObjectContext */
@property(nonatomic, strong)NSManagedObjectContext *context;
- (NSManagedObjectContext *)context {
if (_context == nil) {
// 创建上下文对象,并发队列设置为主队列
_context = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
// 创建托管对象模型,并使用Student.momd路径当做初始化参数
// .xcdatamodeld文件 编译之后变成.momd文件 (.mom文件)
NSURL *modelPath = [[NSBundle mainBundle] URLForResource:@"CoreDataDemo" withExtension:@"momd"];
NSManagedObjectModel *model = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelPath];
// 创建持久化存储调度器
NSPersistentStoreCoordinator *coordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:model];
// 创建并关联SQLite数据库文件,如果已经存在则不会重复创建
NSString *dataPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).lastObject;
dataPath = [dataPath stringByAppendingFormat:@"/%@.sqlite",@"Student"];
[coordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:[NSURL fileURLWithPath:dataPath] options:nil error:nil];
// 上下文对象设置属性为持久化存储器
_context.persistentStoreCoordinator = coordinator;
}
return _context;
}
- 解释说明
(1) 将NSManagedObjectContext
声明为一个变量,并且使用懒加载的形式,方便使用。
(2) .xcdatamodeld
文件 编译之后变成.momd
文件,所以生成NSManagedObjectModel
托管对象模型的时候,文件后缀名一定要写对。
(3) 创建并关联SQLite数据库文件,如果已经存在则不会重复创建。
2.使用系统推荐的方法创建
@property (readonly, strong) NSPersistentContainer *persistentContainer;
@synthesize persistentContainer = _persistentContainer;
- (NSPersistentContainer *)persistentContainer {
// The persistent container for the application. This implementation creates and returns a container, having loaded the store for the application to it.
@synchronized (self) {
if (_persistentContainer == nil) {
_persistentContainer = [[NSPersistentContainer alloc] initWithName:@"CoreDataDemo"];
[_persistentContainer loadPersistentStoresWithCompletionHandler:^(NSPersistentStoreDescription *storeDescription, NSError *error) {
if (error != nil) {
NSLog(@"Unresolved error %@, %@", error, error.userInfo);
abort();
}
}];
}
}
return _persistentContainer;
}
使用的时候
self.persistentContainer.viewContext
四 实战 - 增,删,改,查
4.1 插入操作
CoreData通过NSEntityDescription
的insert
进行插入操作,这样就会生成并返回一个托管对象,并将这个对象插入到上下文中。下面以一个Student
为例:
- (Student *)insert1 {
NSError *error = nil;
// 开始创建托管对象,并指明好创建的托管对象所属实体名
Student * student = [NSEntityDescription insertNewObjectForEntityForName:@"Student" inManagedObjectContext:self.context];
//2.根据表Student中的键值,给NSManagedObject对象赋值
student.name = [NSString stringWithFormat:@"Mr-%d",arc4random() % 100];
student.age = arc4random() % 20;
student.sex = arc4random() % 2 == 0 ? @"男" : @"女" ;
student.height = arc4random() % 180;
student.number = arc4random() % 100;
if ([self.context hasChanges] && ![self.context save:&error]) {
NSLog(@"Unresolved error %@, %@", error, error.userInfo);
_resultLbe.text = [NSString stringWithFormat:@"CoreData Error:%@",error.userInfo];
abort();
return nil;
} else {
_resultLbe.text = @"插入数据成功";
}
return student;
}
NSManagedObjectContext
将操作的数据放到了缓存层中,只有调用了NSManagedObjectContext
的save
后,才会对数据库进行真正的操作,否则对象仅仅存在内存中,这样就很好地避免了数据库的频繁访问。
4.2 删除操作
CoreData首先通过获取需要删除的托管对象,遍历所需要获取的对象数组,逐个删除,最后调用NSManagedObjectContext
的save
方法。
- (void)delete:(NSArray *)delStudents {
// 获取数据的请求对象,指明对实体进行删除操作
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"Student"];
NSMutableArray *delStudentSucces = [NSMutableArray array]; // 保存在数据库中成功被删除的对象
[delStudents enumerateObjectsUsingBlock:^(Student *obj, NSUInteger idx, BOOL *stop) {
// 通过创建谓词对象,然后过滤掉符合要求的对象,也就是要删除的对象
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"number = %d",obj.number];
request.predicate = predicate;
// 通过执行获取操作,找到要删除的对象即可
NSError *error = nil;
NSArray<Student *> *students = [self.context executeFetchRequest:request error:&error];
// 开始真正操作,一一遍历,遍历符合删除要求的对象数组,执行删除操作
[students enumerateObjectsUsingBlock:^(Student *obj, NSUInteger idx, BOOL *stop) {
[self.context deleteObject:obj];
}];
// 错误处理
if (error) {
self.resultLbe.text = [NSString stringWithFormat:@"CoreData Error:%@",error.description];
} else {
[delStudentSucces addObject:obj];
}
}];
// 最后保存数据,保存上下文。
if (self.context.hasChanges) {
[self.context save:nil];
}
if (delStudentSucces.count > 0) {
self.resultLbe.text = @"删除数据成功";
}
// 将已经在数据库中被删除的对象从内存中移除
[delStudentSucces enumerateObjectsUsingBlock:^(Student *obj, NSUInteger idx, BOOL *stop) {
[self.dataSources removeObject:obj];
}];
[self.tableView reloadData];
}
4.3 修改操作
将所有数据库中的学生,性别颠倒,年龄加1操作。
- (void)modify:(NSArray *)modifyStudents {
// 获取数据的请求对象,指明对实体进行删除操作
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"Student"];
NSMutableArray *modifyStudentSucces = [NSMutableArray array];
[modifyStudents enumerateObjectsUsingBlock:^(Student *obj, NSUInteger idx, BOOL *stop) {
// 通过创建谓词对象,然后过滤掉符合要求的对象,也就是要删除的对象
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"number = %d",obj.number];
request.predicate = predicate;
// 通过执行获取操作,找到要删除的对象即可
NSError *error = nil;
NSArray<Student *> *students = [self.context executeFetchRequest:request error:&error];
// 开始真正操作,一一遍历,遍历符合删除要求的对象数组,执行删除操作
[students enumerateObjectsUsingBlock:^(Student *obj, NSUInteger idx, BOOL *stop) {
obj.age += 1;
obj.sex = [obj.sex isEqualToString:@"男"] ? @"女" : @"男";
}];
// 错误处理
if (error) {
self.resultLbe.text = [NSString stringWithFormat:@"CoreData Error:%@",error.description];
} else {
[modifyStudentSucces addObject:obj];
}
}];
// 最后保存数据,保存上下文。
if (self.context.hasChanges) {
[self.context save:nil];
}
if (modifyStudentSucces.count > 0) {
self.resultLbe.text = @"修改数据成功";
}
NSArray *news = [self search];
[self.dataSources removeAllObjects];
[self.dataSources addObjectsFromArray:news];
[self.tableView reloadData];
self.resultLbe.text = @"修改数据成功";
}
4.4 查找操作
查找操作是是有许多条件限制,根据条件查找出相应的数据,下面以一个例子说明一下(查找出所有的元素,条件以后细节会讲出)
- (NSArray *)search {
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"Student"];
// 执行获取操作,获取所有Student托管对象
NSError *error = nil;
NSArray<Student *> *students = [self.context executeFetchRequest:request error:&error];
if (error) {
_resultLbe.text = [NSString stringWithFormat:@"CoreData Error:%@",error.description];
} else {
_resultLbe.text = @"查找数据成功";
}
return students;
}
以上就是CoreData的基本使用,自己也在不断的完善中,希望上面对大家对CoreData认识会进一步提高。
本文参考 iOS-CoreData详解与使用 ,非常感谢该作者。
更多同类型文章参考
iOS-SQLite3的使用详解
iOS-CoreData详解与使用
iOS-FMDB详解及使用
iOS SQLite、CoreData、FMDB数据库详解