coreData详解

发布时间:2017-1-23 12:33:30 编辑:www.fx114.net 分享查询网我要评论
本篇文章主要介绍了"coreData详解 ",主要涉及到coreData详解 方面的内容,对于coreData详解 感兴趣的同学可以参考一下。

1、初识CoreData

CoreData的结构构成:

NSManagedObjectModel的构成:

  可以通过Entity创建继承自NSManagedObject类的文件,这个文件就是开发中使用的托管对象,具备模型对象的表示功能,CoreData的本地持久化都是通过这个类及其子类完成的。

  在CoreData的整体结构中,主要分为两部分。一个是NSManagedObjectContext管理的模型部分,管理着所有CoreData的托管对象。一个是SQLite实现的本地持久化部分,负责和SQL数据库进行数据交互,主要由NSPersistentStore类操作。这就构成了CoreData的大体结构。


  从图中可以看出,这两部分都是比较独立的,两部分的交互由一个持久化存储调度器(NSPersistentStoreCoordinator)来控制。上层NSManagedObjectContext存储的数据都是交给持久化调度器,由调度器调用具体的持久化存储对象(NSPersistentStore)来操作对应的数据库文件,NSPersistentStore负责存储的实现细节。这样就很好的将两部分实现了分离。

2、认识CoreData-基础使用

  在模型文件的实体中,参数类型和平时创建继承自NSObject的模型类大体类似,但是还是有一些关于类型的说明,下面简单的列举了一下。

  • 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协议

  在实体最下面,有一个Fetched Properties选项,这个选项用的不多,这里就不细讲了。Fetched Properties用于定义查询操作,和NSFetchRequest功能相同。定义fetchedProperty对象后,可以通过NSManagedObjectModel类的fetchRequestFromTemplateWithName:substitutionVariables:方法或其他相关方法获取这个fetchedProperty对象。

获取这个对象后,系统会默认将这个对象缓存到一个字典中,缓存之后也可以通过fetchedProperty字典获取fetchedProperty对象。

属性设置:

  • default Value: 设置默认值,除了二进制不能设置,其他类型几乎都能设置。

  • optional: 在使用时是否可选,也可以理解为如果设置为NO,只要向MOC进行save操作,这个属性是否必须有值。否则MOC进行操作时会失败并返回一个error,该选项默认为YES

  • transient: 设置当前属性是否只存在于内存,不被持久化到本地,如果设置为YES,这个属性就不参与持久化操作,属性的其他操作没有区别。transient非常适合存储一些在内存中缓存的数据,例如存储临时数据,这些数据每次都是不同的,而且不需要进行本地持久化,所以可以声明为transient的属性。

  • indexed: 设置当前属性是否是索引。添加索引后可以有效的提升检索操作的速度。但是对于删除这样的操作,删除索引后其他地方还需要做出相应的变化,所以速度会比较慢。

  • Validation: 通过Validation可以设置Max ValueMin Value,通过这两个条件来约定数据,对数据的存储进行一个验证。数值类型都有相同的约定方式,而字符串则是约定长度,date是约定时间。

  • Reg. Ex.(Regular Expression): 可以设置正则表达式,用来验证和控制数据,不对数据自身产生影响。(只能应用于String类型)

  • Allows External Storage: 当存储二进制文件时,如果遇到比较大的文件,是否存储在存储区之外。如果选择YES,存储文件大小超过1MB的文件,都会存储在存储区之外。否则大型文件存储在存储区内,会造成SQLite进行表操作时,效率受到影响。

Relationships设置:

  • delete rule: 定义关联属性的删除规则。在当前对象和其他对象有关联关系时,当前对象被删除后与之关联对象的反应。这个参数有四个枚举值,代码对应着模型文件的相同选项。

    NSNoActionDeleteRule 删除后没有任何操作,也不会将关联对象的关联属性指向nil。删除后使用关联对象的关联属性,可能会导致其他问题。

    NSNullifyDeleteRule 删除后会将关联对象的关联属性指向nil,这是默认值。

    NSCascadeDeleteRule 删除当前对象后,会将与之关联的对象也一并删除。

    NSDenyDeleteRule 在删除当前对象时,如果当前对象还指向其他关联对象,则当前对象不能被删除。

  • Type: 主要有两种类型,To OneTo Many,表示当前关系是一对多还是一对一。

实体:

  • Parent Entity: 可以在实体中创建继承关系,在一个实体的菜单栏中通过Parent Entity可以设置父实体,这样就存在了实体的继承关系,最后创建出来的托管模型类也是具有继承关系的。注意继承关系中属性名不要相同。

  使用了这样的继承关系后,系统会将子类继承父类的数据,存在父类的表中,所有继承自同一父类的子类都会将父类部分存放在父类的表中。这样可能会导致父类的表中数据量过多,造成性能问题。


2、CoreData-基础使用

Fetched Properties

  在实体最下面,有一个Fetched Properties选项,这个选项用的不多,这里就不细讲了。Fetched Properties用于定义查询操作,和NSFetchRequest功能相同。定义fetchedProperty对象后,可以通过NSManagedObjectModel类的fetchRequestFromTemplateWithName:substitutionVariables:方法或其他相关方法获取这个fetchedProperty对象。

获取这个对象后,系统会默认将这个对象缓存到一个字典中,缓存之后也可以通过fetchedProperty字典获取fetchedProperty对象。

Fetch Requests

  在模型文件中Entities下面有一个Fetch Requests,这个也是配置请求对象的。但是这个使用起来更加直观,可以很容易的完成一些简单的请求配置。相对于上面讲到的Fetched Properties,这个还是更方便使用一些。



  上面是对Employee实体的height属性配置的Fetch Request,这里配置的height小于2米。配置之后可以通过NSManagedObjectModel类的fetchRequestTemplateForName:方法获取这个请求对象,参数是这个请求配置的名称,也就是EmployeeFR

CoreData增删改查

- (IBAction)SchoolAdd:(UIButton *)sender {    // 创建托管对象,并指明创建的托管对象所属实体名    _student = [NSEntityDescription insertNewObjectForEntityForName:@"Student" inManagedObjectContext:[CoreDataManager sharedCoreDataManager].persistentContainer.viewContext];    _student.name = @"lxz";    // 实体中所有基础数据类型,创建类文件后默认都是NSNumber类型的    _student.age = @(23);        // 通过上下文保存对象,并在保存前判断是否有更改    NSError * error = nil;    if ([CoreDataManager sharedCoreDataManager].persistentContainer.viewContext.hasChanges) {        BOOL isAddSuccess = [[CoreDataManager sharedCoreDataManager].persistentContainer.viewContext save:&error];        if (isAddSuccess) {            NSLog(@"SchoolisAddSuccess");        }    }        // 错误处理,可以在这实现自己的错误处理逻辑    if (error) {        NSLog(@"CoreData Insert Data Error : %@", error);    }}
- (IBAction)SchoolDelete:(UIButton *)sender {    // 建立获取数据的请求对象,指明对Student实体进行删除操作    NSFetchRequest * request = [NSFetchRequest fetchRequestWithEntityName:@"Student"];    // 创建谓词对象,过滤出符合要求的对象,也就是要删除的对象    NSPredicate * predicate = [NSPredicate predicateWithFormat:@"name = %@", @"lxz"];    // 执行获取操作,找到要删除的对象    NSError * error = nil;    NSArray<Student *> * students = [[CoreDataManager sharedCoreDataManager].persistentContainer.viewContext executeFetchRequest:request error:&error];    // 遍历符合删除要求的对象数组,执行删除操作    [students enumerateObjectsUsingBlock:^(Student * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {        [[CoreDataManager sharedCoreDataManager].persistentContainer.viewContext deleteObject:obj];    }];    // 保存上下文,并判断当前上下文是否有改动    if ([CoreDataManager sharedCoreDataManager].persistentContainer.viewContext.hasChanges) {        BOOL isDeleteSuccess = [[CoreDataManager sharedCoreDataManager].persistentContainer.viewContext save:nil];        if (isDeleteSuccess) {            NSLog(@"SchoolisDeleteSuccess");        }    }    // 错误处理    if (error) {        NSLog(@"CoreData Delete Data Error : %@", error);    }}
- (IBAction)SchoolUpdate:(UIButton *)sender {    // 建立获取数据的请求对象,并指明操作的实体为Student    NSFetchRequest * request = [NSFetchRequest fetchRequestWithEntityName:@"Student"];    // 创建谓词对象,设置过滤条件    NSPredicate * predicate = [NSPredicate predicateWithFormat:@"name = %@", @"lxz"];    request.predicate = predicate;        // 执行获取请求,获取到符合要求的托管对象    NSError * error = nil;    NSArray<Student *> *students = [[CoreDataManager sharedCoreDataManager].persistentContainer.viewContext executeFetchRequest:request error:&error];        // 遍历获取到的数组,并执行修改操作    [students enumerateObjectsUsingBlock:^(Student * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {        obj.age = @(24);    }];        // 将上面的修改进行存储    if ([CoreDataManager sharedCoreDataManager].persistentContainer.viewContext.hasChanges) {        BOOL isUpdateSuccess = [[CoreDataManager sharedCoreDataManager].persistentContainer.viewContext save:nil];        if (isUpdateSuccess) {            NSLog(@"SchoolIsUpdateSuccess");        }    }        // 错误处理    if (error) {        NSLog(@"CoreData Update Data Error : %@", error);    }        /**     在上面简单的设置了NSPredicate的过滤条件,对于比较复杂的业务需求,还可以设置复合过滤条件,例如下面的例子     [NSPredicate predicateWithFormat:@"(age < 25) AND (firstName = XiaoZhuang)"]          也可以通过NSCompoundPredicate对象来设置复合过滤条件     [[NSCompoundPredicate alloc] initWithType:NSAndPredicateType subpredicates:@[predicate1, predicate2]]     */}
- (IBAction)SchoolSearch:(UIButton *)sender {    // 建立获取数据的请求对象,指明操作的实体为Student    NSFetchRequest * request = [NSFetchRequest fetchRequestWithEntityName:@"Student"];        // 执行获取操作,获取所有Student托管对象    NSError * error = nil;    NSArray<Student *> * students = [[CoreDataManager sharedCoreDataManager].persistentContainer.viewContext executeFetchRequest:request error:&error];        // 遍历输出查询结果    [students enumerateObjectsUsingBlock:^(Student * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {        NSLog(@"Student Name : %@, Age : %d", obj.name, obj.age);    }];        // 错误处理    if (error) {        NSLog(@"CoreData Ergodic Data Error : %@", error);    }}

 

3、CoreData-使用进阶

  CoreData中可以通过设置NSFetchRequest类的predicate属性,来设置一个NSPredicate类型的谓词对象当做过滤条件。通过设置这个过滤条件,可以只获取符合过滤条件的托管对象,不会将所有托管对象都加载到内存中。这样是非常节省内存和加快查找速度的,设计一个好的NSPredicate可以优化CoreData搜索性能。

[NSPredicate predicateWithFormat:@"age >= 30"]

  可以通过NSPredicateiOS中的集合对象执行过滤操作,可以是NSArrayNSSet及其子类。对不可变数组NSArray执行的过滤,过滤后会返回一个NSArray类型的结果数组,其中存储着符合过滤条件的对象。

NSArray *results = [array filteredArrayUsingPredicate:predicate]

谓词不只可以过滤简单条件,还可以过滤复杂条件,设置复合过滤条件。

[NSPredicate predicateWithFormat:@"(age < 25) AND (firstName = XiaoZhuang)"]

当然也可以通过NSCompoundPredicate对象来设置复合过滤条件,返回结果是一个NSPredicate的子类NSCompoundPredicate对象。

[[NSCompoundPredicate alloc] initWithType:NSAndPredicateType subpredicates:@[predicate1, predicate2]]

NSPredicate中还可以使用正则表达式,可以通过正则表达式完成一些复杂需求,这使得谓词的功能更加强大,例如下面是一个手机号验证的正则表达式

NSString *mobile = @"^1(3[0-9]|5[0-35-9]|8[025-9])\\d{8}$";NSPredicate *regexmobile = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", mobile];

NSPredicate支持对数据的模糊查询,例如下面使用通配符来匹配包含lxz的结果,具体CoreData中的使用在下面会讲到。

[NSPredicate predicateWithFormat:@"name LIKE %@", @"*lxz*"]

NSPredicate在创建查询条件时,还支持设置被匹配目标的keyPath,也就是设置更深层被匹配的目标。例如下面设置employeename属性为查找条件,就是用点语法设置的keyPath

[NSPredicate predicateWithFormat:@"employee.name = %@", @"lxz"]

在执行fetch操作前,可以给NSFetchRequest设置一些参数,这些参数包括谓词、排序等条件,下面是一些基础的设置。

  • 设置查找哪个实体,从数据库的角度来看就是查找哪张表,通过fetchRequestWithEntityName:或初始化方法来指定表名。
  • 通过NSPredicate类型的属性,可以设置查找条件,这个属性在开发中用得最多。NSPredicate可以包括固定格式的条件以及正则表达式
  • 通过sortDescriptors属性,可以设置获取结果数组的排序方式,这个属性是一个数组类型,也就是可以设置多种排序条件。(但是注意条件不要冲突)
  • 通过fetchOffset属性设置从查询结果的第几个开始获取,通过fetchLimit属性设置每次获取多少个。主要用于分页查询,后面会讲。

  MOC执行fetch操作后,获取的结果是以数组的形式存储的,数组中存储的就是托管对象。NSFetchRequest提供了参数resultType,参数类型是一个枚举类型。通过这个参数,可以设置执行fetch操作后返回的数据类型。

设置获取条件

// 建立获取数据的请求对象,并指明操作Employee表NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"Employee"];// 设置请求条件,通过设置的条件,来过滤出需要的数据NSPredicate *predicate = [NSPredicate predicateWithFormat:@"name = %@", @"lxz"];request.predicate = predicate;// 设置请求结果排序方式,可以设置一个或一组排序方式,最后将所有的排序方式添加到排序数组中NSSortDescriptor *sort = [NSSortDescriptor sortDescriptorWithKey:@"height" ascending:YES];// NSSortDescriptor的操作都是在SQLite层级完成的,不会将对象加载到内存中,所以对内存的消耗是非常小的request.sortDescriptors = @[sort];// 执行获取请求操作,获取的托管对象将会被存储在一个数组中并返回NSError *error = nil;NSArray<Employee *> *employees = [context executeFetchRequest:request error:&error];[employees enumerateObjectsUsingBlock:^(Employee * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {    NSLog(@"Employee Name : %@, Height : %@, Brithday : %@", obj.name, obj.height, obj.brithday);}];// 错误处理if (error) {    NSLog(@"CoreData Fetch Data Error : %@", error);}

这里设置NSFetchRequest对象的一些请求条件,设置查找Employee表中namelxz的数据,并且将所有符合的数据用height升序的方式排列。

查询操作

// 创建获取数据的请求对象,并指明操作Department表NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"Department"];// 设置请求条件,设置employee的name为请求条件。NSPredicate的好处在于,可以设置keyPath条件NSPredicate *predicate = [NSPredicate predicateWithFormat:@"employee.name = %@", @"lxz"];request.predicate = predicate;// 执行查找操作NSError *error = nil;NSArray<Department *> *departments = [context executeFetchRequest:request error:&error];[departments enumerateObjectsUsingBlock:^(Department * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {    NSLog(@"Department Search Result DepartName : %@, employee name : %@", obj.departName, obj.employee.name);}];// 错误处理if (error) {    NSLog(@"Department Search Error : %@", error);}

查找Department实体,并打印实体内容。就像上面讲的双向关系一样,有关联关系的实体,自己被查找出来后,也会将与之关联的其他实体也查找出来,并且查找出来的实体都是关联着MOC的。

分页查询

在从本地存储区获取数据时,可以指定从第几个获取,以及本次查询获取多少个数据,联合起来使用就是分页查询。当然也可以根据需求,单独使用这两个API

这种需求在实际开发中非常常见,例如TableView中,上拉加载数据,每次加载20条数据,就可以利用分页查询轻松实现。

#pragma mark - ----- Page && Fuzzy ------//分页查询- (IBAction)pageSearch:(UIButton *)sender {    // 创建获取数据的请求对象,并指明操作Student表    NSFetchRequest * request = [NSFetchRequest fetchRequestWithEntityName:@"Student"];        // 设置查找起始点,这里是从搜索结果的第六个开始获取    request.fetchOffset = 6;        // 设置分页,每次请求获取六个托管对象    request.fetchLimit = 6;        // 设置排序规则,这里设置年龄升序排序    NSSortDescriptor * descriptor = [NSSortDescriptor sortDescriptorWithKey:@"age" ascending:YES];    request.sortDescriptors = @[descriptor];        // 执行查询操作    NSError * error = nil;    NSArray<Student *> *students = [[CoreDataManager sharedCoreDataManager].persistentContainer.viewContext executeFetchRequest:request error:&error];        // 遍历输出查询结果    [students enumerateObjectsUsingBlock:^(Student * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {        NSLog(@"Page Search Result Name : %@, Age : %d", obj.name, obj.age);    }];        // 错误处理    if (error) {        NSLog(@"Page Search Data Error : %@", error);    }}

上面是一个按照身高升序排序,分页获取搜索结果的例子。查找Employee表中的实体,将结果按照height字段升序排序,并从结果的第六个开始查找,并且设置获取的数量也是六个。

模糊查询

有时需要获取具有某些相同特征的数据,这样就需要对查询的结果做模糊匹配。在CoreData执行模糊匹配时,可以通过NSPredicate执行这个操作。

//模糊查询- (IBAction)fuzzySearch:(UIButton *)sender {    // 创建获取数据的请求对象,设置对Student表进行操作    NSFetchRequest * request = [NSFetchRequest fetchRequestWithEntityName:@"Student"];        // 创建模糊查询条件。这里设置的带通配符的查询,查询条件是结果包含lxz    NSPredicate * predicate = [NSPredicate predicateWithFormat:@"name LIKE %@", @"*lxz*"];    request.predicate      = predicate;        // 执行查询操作    NSError * error = nil;    NSArray<Student *> *students = [[CoreDataManager sharedCoreDataManager].persistentContainer.viewContext executeFetchRequest:request error:&error];        // 遍历输出查询结果    [students enumerateObjectsUsingBlock:^(Student * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {        NSLog(@"Fuzzy Search Result Name : %@, Age : %d", obj.name, obj.age);    }];        // 错误处理    if (error) {        NSLog(@"Fuzzy Search Data Error : %@", error);    }        /**     模糊查询的关键在于设置模糊查询条件,除了上面的模糊查询条件,还可以设置下面三种条件     */    // 以lxz开头    // NSPredicate *predicate1 = [NSPredicate predicateWithFormat:@"name BEGINSWITH %@", @"lxz"];    // 以lxz结尾    // NSPredicate *predicate2 = [NSPredicate predicateWithFormat:@"name ENDSWITH %@"  , @"lxz"];    // 其中包含lxz    // NSPredicate *predicate3 = [NSPredicate predicateWithFormat:@"name contains %@"  , @"lxz"];    // 还可以设置正则表达式作为查找条件,这样使查询条件更加强大,下面只是给了个例子    // NSString *mobile = @"^1(3[0-9]|5[0-35-9]|8[025-9])\\d{8}$";    // NSPredicate *predicate4 = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", mobile];}

上面是使用通配符的方式进行模糊查询NSPredicate支持多种形式的模糊查询,下面列举一些简单的匹配方式。模糊查询条件对大小写不敏感,所以查询条件大小写均可。

加载请求模板

在之前的文章中谈到在模型文件中设置请求模板,也就是在.xcdatamodeld文件中,设置Fetch Requests,使用时可以通过对应的NSManagedObjectModel获取设置好的模板。

#pragma mark - ----- Fetch Request ------/** 加载模型文件中设置的FetchRequest请求模板,模板名为StudentAge,在School.xcdatamodeld中设置 */- (IBAction)fetchRequest:(UIButton *)sender {    // 通过MOC获取托管对象模型,托管对象模型相当于.xcdatamodeld文件,存储着.xcdatamodeld文件的结构    NSManagedObjectModel * model = [CoreDataManager sharedCoreDataManager].persistentContainer.managedObjectModel;        // 通过.xcdatamodeld文件中设置的模板名,获取请求对象    NSFetchRequest * fetchRequest = [model fetchRequestTemplateForName:@"StudentAge"];        // 请求数据,下面的操作和普通请求一样    NSError *error = nil;    NSArray<Student *> *dataList = [[CoreDataManager sharedCoreDataManager].persistentContainer.viewContext executeFetchRequest:fetchRequest error:&error];        // 遍历获取结果,并打印结果    [dataList enumerateObjectsUsingBlock:^(Student * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {        NSLog(@"Student.count = %ld, Student.age = %d", dataList.count, obj.age);    }];        // 错误处理    if (error) {        NSLog(@"Execute Fetch Request Error : %@", error);    }

上一篇:iOS应用之间的跳转
下一篇:CentOS7安装docker

相关文章

关键词: coreData详解

相关评论