前言
- 这篇记录主要记录我学习Core Data的过程,其中在原始记录上做了很多次的补充,并且已经标注出来,以此来表示学习是循序而进的一个过程。
- 这个记录适合想初识Core Data的同伴。
- 我真的想把它称为史上最人性化的Core Data总结,所谓人性化就是用的是小白能听懂的语言。
- 文章很长,或许有点枯燥,其实我写的时候是充满了激情的,语言或许有点搞笑。
- 最后,请耐心阅读。
内容概要
- Xcode 8 和Xcode 10 创建时的步骤。
- Xcode 10 比Xcode 8 多的内容。
- 增删改查的具体代码
使用Core Data的步骤:(Xcode 10)
第一种,从项目开始就知道我要使用Core Data
创建项目的时候把Use CoreData 勾上
会生成
.xcdatamodeld
文件.
关于这个名字我是这么理解的:Xcode 的 Core Data 的 model
简写就是xc(xcode)data(coredata)modeld(这个词网络义是“建模”),这样这个名字就很容易被记住了。我的理解就是他的文件名,Core Data的模型。
- 第二次补充:我将“Core Data的模型”画掉了,因为第一遍理解的不对。xcdatamodeld是关于模型的文件,可以利用这个文件生成模型的对象,再利用模型的对象生成实例。
点击界面最下边的add entity,可以添加一个实体。双击可以改实体的名字,名字第一个英文字母要大写,否则会报警告,看着多难受,而且也会没有办法进行下一步,强制要求你大写,所以还是乖乖的大写好了。
关于这个实体的解释,我把它看做一个表。xcdatamodeld文件里面有3个部分:Attributes,relationships , Fetched Proerties.
Attributes:属性,可以理解为一张表的属性。就比如年龄,身高。
relationships:我目前只知道是关系,表和表的关系,有一对多,多对多。但是怎么设置我不知道。后面会了再补充。
Fetched Proerties:我目前是一点也不知道,这么鬼东西。.找到并执行Create NSManagedObject SubClass,创建 NSManagedObject实体类,会有4个分类。他们叫做托管实体类,这是什么东东,我真的不知道。
大神总结的几个类的含义,我反正没有理解。
NSManagedObjectContext 上下文
NSManagedObjectModel 数据模型
NSPersistentStoreCoordinator 使用它来添加数据库,设置储存有关的内容
NSManagedObject 被管理的数据记录
NSFetchRequest 数据请求
NSEntityDescription 表格实体结构
第二种:自己项目写一半了才知道必须要创建一个coredata了
1.先去commond+n创建一个Data Model文件
2.我们需要手动写代码,创建数据库,关联数据库,还要加在上下文中。
先将手动创建的代码展示出来
NSURL *modelURL = [[NSBundle mainBundle] URLForResource:@"Model" withExtension:@"momd"];
NSManagedObjectModel *model = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
NSPersistentStoreCoordinator *store = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:model];
NSString *docStr = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
NSString *sqlPath = [docStr stringByAppendingPathComponent:@"coreData.sqlite"];
NSURL *sqlUrl = [NSURL fileURLWithPath:sqlPath];
NSError *error = nil;
[store addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:sqlUrl options:nil error:&error];
if (error) {
NSLog(@"添加数据库失败:%@",error);
} else {
NSLog(@"添加数据库成功");
}
NSManagedObjectContext *context = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
context.persistentStoreCoordinator = store;
_context = context;
}
我对代码的解读:
我们要创建数据库肯定得根据我们的模型去创建,包括数据库名字里面有哪些字段属性。
这时候我们的.xcdatamodeld文件的作用就是这个,模型的信息全被保存到一个后缀为momd的文件中。NSManagedObjectModel这个类就用到了:首先找到.momd文件的路径,根据路径找到模型文件利用NSManagedObjectModel类将模型文件中的信息真的变成模型对象。再根据模型去创建对象。
之后就考虑到了数据的持久化,所谓持久化,就是写个sqlite文件,将数据保存起来,不过,保存下来的数据都是按照模型的标准保存的。我们用到的类是NSPersistentStoreCoordinator ,调用
[store addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:sqlUrl options:nil error:&error];
方法。传入的参数就是采用什么样的存储方式,以及对应文件的URL 。
基本都是用sqlite,传入sqlite url地址。就能根据模型路径创建好sqlite文件。
最后一步是创建上下文,将上下文关联持久化助理(其实就是关联好了数据库)。
使用Core Data的步骤:(Xcode 8)
勾选了Core Data 后,将原来的.xcdatamodeld
文件删掉,自己手动创建一个data model文件,后面和Xcode10手动创建的步骤一样。
Xcode 10 比 Xcode 8 多的内容
介绍
Xcode 10相比于Xcode 8 多了一个类:NSPersistentContainer
。
咱去查查它的英文意思,persistent(不断的,坚持的)Container(容器)总结下来就是持久容器。
但是大神对这个类的总结是:封装了应用程序中的CoreData Stack(核心数据栈堆),简化了创建和管理的核心堆栈的数据处理创建。关于核心数据栈堆???什么鬼?
- 第二次补充:
NSPersistentContainer
是一个容器,封装了应用程序中的CoreData Stack
实例化有4中方法:2种类方法,2种对象方法
//name 表示数据库的名称,这个方法其实直接帮我们创建了名称为name一个数据库,并且关联到NSPersistentContainer //model是根据模型来创建的数据库
+(instancetype)persistentContainerWithName:(NSString *)name;
+(instancetype)persistentContainerWithName:(NSString *)name managedObjectModel:(NSManagedObjectModel *)model
对象方法和类方法是一样的,无非就把+
换成-
可访问属性
//返回沙盒中存储数据库的文件夹URL路径
+ (NSURL *)defaultDirectoryURL;
//只读属性,当前NSPersistentContainer容器的名称
@property (copy, readonly) NSString *name;
//只读属性,自动生成的管理对象上下文,这个上下文默认的操作类型是NSMainQueueConcurrencyType主线程
***** NSManagedObjectContext 加5颗星很重要*****
@property (strong, readonly) NSManagedObjectContext *viewContext;
//只读属性,对象管理模型
@property (strong, readonly) NSManagedObjectModel *managedObjectModel;
//只读属性,存储调度器
@property (strong, readonly) NSPersistentStoreCoordinator *persistentStoreCoordinator;
配置信息
//加载存储器,此方法必须要调用,否则无法存储数据 //block中NSPersistentStoreDescription用于描述生成的存储器信息,如:数据库文件路径、存储类型等 NSError用于描述加载存储器是否成功或失败信息
- (void)loadPersistentStoresWithCompletionHandler:(void (^)(NSPersistentStoreDescription *, NSError * _Nullable))block;
//返回一个基于多线程的管理对象上下文,我们无需关心多线程的内部实现以及线程安全,由NSPersistentContainer新创建一个
//此方法非常牛逼,调用这个方法之后,对返回的上下文做一些数据的处理都是在子线程中完成的,可以用于处理对数据库进行大量数据操作的场景
(NSManagedObjectContext *)newBackgroundContext NS_RETURNS_RETAINED;
源码分析
在APPDelegate.h文件中新增了
@property (readonly, strong) NSPersistentContainer *persistentContainer;
- (void)saveContext;
在APPDelegate.m中新增了persistentContainer get
方法
- (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) {
//创建一个数据库的容器
// name:表示保存的数据库文件名称
// 使用此方法创建NSPersistentContainer,默认模型文件名称为name
_persistentContainer = [[NSPersistentContainer alloc] initWithName:@"sqlite"];
//加载存储器,此方法必须要调用,否则无法存储数据
//block中NSPersistentStoreDescription用于描述生成的存储器信息,如:数据库文件路径、存储类型等 NSError用于描述加载存储器是否成功或失败信息
[_persistentContainer loadPersistentStoresWithCompletionHandler:^(NSPersistentStoreDescription *storeDescription, NSError *error) {
if (error != nil) {
NSLog(@"Unresolved error %@, %@", error, error.userInfo);
//语言的函数,强制退出
abort();
}
}];
}
}
return _persistentContainer;
}
- (void)saveContext {
// 取出上下文
NSManagedObjectContext *context = self.persistentContainer.viewContext;
NSError *error = nil;
if ([context hasChanges] && ![context save:&error]) {
NSLog(@"Unresolved error %@, %@", error, error.userInfo);
abort();
}
}
具体的增删改查的代码
- 增
Dog *dog = [NSEntityDescription insertNewObjectForEntityForName:@"Dog" inManagedObjectContext:_app.persistentContainer.viewContext];
static NSInteger index = 0;
dog.name = [NSString stringWithFormat:@"tom%ld",index++];
dog.sex = @"公";
dog.age = [NSString stringWithFormat:@"%uyears",arc4random() % 15];
[_app saveContext];
关于NSEntityDescription
类
每一个data model对应一个NSEntityDescription对象,NSEntityDescription可以根据传入的EntityName找到对应的模型生成一个NSManagedObject实体。
最后要调用APPDelegate的saveContext
方法。
- 查
NSEntityDescription *entity = [NSEntityDescription entityForName:@"Dog" inManagedObjectContext:_app.persistentContainer.viewContext];
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:entity];
NSArray *array = [_app.persistentContainer.viewContext executeFetchRequest :request error:nil];
for (Dog *dog in array) {
NSLog(@"dog name:%@",dog.name);
}
}
关于NSFetchRequest
类,在介绍这个之前要先介绍一下NSPredicate
这个类
它叫做“谓词”,可以对数组根据指定条件筛选最后以数组的形式返回。
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"过滤条件"];
有两种过滤方法:1.将数组单个取出过滤。2.对数组过滤。代码分别为:
[predicate evaluateWithObject:model];
NSArray *results = [array filteredArrayUsingPredicate:predicate];
过滤条件想深入了解请戳这里https://blog.csdn.net/fengsh998/article/details/8125263
NSFetchRequest
的实例描述用于从持久性存储检索数据的搜索条件。
常用方法:
-setEntity:设置你要查询的数据对象的类型(Entity)你需要创建一个NSEntityDescription对象
-setPredicate:设置查询条件,你需要创建一个NStPredicate对象
-setFetchLimit:设置最大查询对象数目,你需要创建一个NStPredicate对象
-setSortDescriptors:设置查询结果的排序方法,你需要创建一个NSSortDescriptors对象
好的,再来补充一下NSSortDescriptors
很多集合都提供排序的方法EM:NSSet,NSArray,NSMutableArray,NSOrderedSet,NSMutableOrderedSet
NSSet
-(NSArray *)sortedArrayUsingDescriptors:(NSArray *)sortDescriptors
NSArray
-(NSArray *)sortedArrayUsingDescriptors:(NSArray *)sortDescriptors;
NSMutableArray
-(void)sortUsingDescriptors:(NSArray *)sortDescriptors;
NSOrderedSet
-(NSArray *)sortedArrayUsingDescriptors:(NSArray *)sortDescriptors
NSMutableOrderedSet
-(void)sortUsingDescriptors:(NSArray *)sortDescriptor
例子:
NSSortDescriptor *sort = [NSSortDescriptor sortDescriptorWithKey:@"age" ascending:YES];
//key : 排序key, 某个对象的属性名称,ascending : 是否升序, YES-升序, NO-降序
[peoples sortUsingDescriptors:@[sort]];
// 输出排序结果,如果需要多个排序条件,再创建一个NSSortDescriptor对象,在@[sort]后添加例如sort1.
for (People *people in peoples) {
NSLog(@"age: %d,height: %f name: %@", people.age,people.height, people.name);
}
但是,我又遇到没有见过的NSOrderedSet,NSMutableOrderedSet
- NSSet按Hash算法来存储集合中的元素,因此具有很好的存取和查找性能。
- NSSet不保证元素的添加顺序,顺序有可能发生变化。
- 与NSArray相比最大区别是元素没有索引
- 元素不可重复
想详细了解戳这里https://www.jianshu.com/p/db6395cd9bfd
- 更
-(void)updateCoreData{
NSEntityDescription *entity = [NSEntityDescription entityForName:@"Dog" inManagedObjectContext:_app.persistentContainer.viewContext];
NSFetchRequest *request = [[NSFetchRequest alloc] init];
request.entity = entity;
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"name=%@",@"tom0"];
request.predicate = predicate;
NSArray *array = [_app.persistentContainer.viewContext executeFetchRequest:request error:nil];
if (array.count) {
for (Dog *dog in array) {
dog.name = @"jerry8";
}
[_app saveContext];
NSLog(@"UPDATA SUCCESS!!");
} else
NSLog(@"NOT FOUND DATA");
}
这段代码先根据给定条件查询到对应的数据组,再进行更改。
- 删
-(void)deleteCoreData{
NSEntityDescription *entity = [NSEntityDescription entityForName:@"Dog" inManagedObjectContext:_app.persistentContainer.viewContext];
NSFetchRequest *request = [[NSFetchRequest alloc] init];
request.entity = entity;
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"name=%@",@"tom0"];
request.predicate = predicate;
NSArray *array = [_app.persistentContainer.viewContext executeFetchRequest:request error:nil];
if (array.count) {
for (Dog *dog in array) {
[_app.persistentContainer.viewContext deleteObject:dog];
}
NSLog(@"DELETE SUCCESS!!");
} else
NSLog(@"NOT FOUND DATA");
}
最重要的就是调用上下文的deleteObjec
t方法