认识CoreData-使用进阶

该文章属于<简书 — 刘小壮>原创,转载请注明:

<简书 — 刘小壮> http://www.jianshu.com/p/a4710356244d

之前两篇文章都比较偏理论,文字表达比较多一些,但都是干货!学习时先理解理论知识,才能更好的帮助后面的理解。

在这篇文章中,将会涉及关于CoreData的一些复杂操作,这些操作会涉及分页查询、模糊查询、批处理等高级操作。 通过这些操作可以更好的使用CoreData,提升CoreData性能。文章中将会出现大量示例代码,通过代码的方式更有助于理解。 文章内容还会比较多,希望各位耐心看完。

文章中如有疏漏或错误,还请各位及时提出,谢谢!


NSPredicate

概述

iOS开发过程中,很多需求都需要用到过滤条件。例如过滤一个集合对象中存储的对象,可以通过Foundation框架下的NSPredicate类来执行这个操作。

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

语法

NSPredicate更加偏向于自然语言,不像SQLite一样有很多固定的语法,看起来也更加清晰易懂。例如下面需要查找条件为年龄30岁以上,并且包括30岁的条件。

[NSPredicate predicateWithFormat:@"age >= 30"]
过滤集合对象

可以通过NSPredicateiOS中的集合对象执行过滤操作,可以是NSArrayNSSet及其子类。

对不可变数组NSArray执行的过滤,过滤后会返回一个NSArray类型的结果数组,其中存储着符合过滤条件的对象。

NSArray *results = [array filteredArrayUsingPredicate:predicate]

对可变数组NSMutableArray执行的过滤条件,过滤后会直接改变原集合对象内部存储的对象,删除不符合条件的对象。

[arrayM filterUsingPredicate:predicate]
复合过滤条件

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

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

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

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

枚举值NSCompoundPredicateType参数,可以设置三种复合条件,枚举值非常直观很容易看懂。

  • NSNotPredicateType
  • NSAndPredicateType
  • NSOrPredicateType
基础语法

下面是列举的一些NSPredicate的基础语法,这些语法看起来非常容易理解,更复杂的用法可以去看苹果的官方API

语法作用
==判断是否相等
>=大于或等于
<=小于或等于
>大于
<小于
!=不等于
AND 或 &&
OR 或 II
NOT 或 !
正则表达式

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*"]
keyPath

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

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

设置查询条件

在之前的文章中,执行下面MOCfetchRequest方法,一般都需要传入一个NSFetchRequest类型的参数。这个request参数可以做一些设置操作,这样就可以以较优的性能获取指定的数据。

- (nullable NSArray *)executeFetchRequest:(NSFetchRequest *)request error:(NSError **)error;
NSFetchRequest

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

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

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

  • NSManagedObjectResultType: 返回值是NSManagedObject的子类,也就是托管对象,这是默认选项
  • NSManagedObjectIDResultType: 返回NSManagedObjectID类型的对象,也就是NSManagedObjectID,对内存占用比较小。MOC可以通过NSManagedObjectID对象获取对应的托管对象,并且可以通过缓存NSManagedObjectID参数来节省内存消耗
  • NSDictionaryResultType: 返回字典类型对象
  • NSCountResultType: 返回请求结果的count值,这个操作是发生在数据库层级的,并不需要将数据加载到内存中
设置获取条件
// 建立获取数据的请求对象,并指明操作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升序的方式排列。

有实体关联关系

一个模型文件中的不同实体间,可以设置实体间的关联关系,这个在之前的文章中讲过。实体关联关系分为对一对多,也可以设置是否双向关联

这里演示的实体只是简单的To One的关系,并且下面会给出设置是否双向关联的区别对比。

插入实体
// 创建托管对象,并将其关联到指定的MOC上
Employee *zsEmployee = [NSEntityDescription insertNewObjectForEntityForName:@"Employee" inManagedObjectContext:context];
zsEmployee.name = @"zhangsan";
zsEmployee.height = @1.9f;
zsEmployee.brithday = [NSDate date];

Employee *lsEmployee = [NSEntityDescription insertNewObjectForEntityForName:@"Employee" inManagedObjectContext:context];
lsEmployee.name = @"lisi";
lsEmployee.height = @1.7f;
lsEmployee.brithday = [NSDate date];

Department *iosDepartment = [NSEntityDescription insertNewObjectForEntityForName:@"Department" inManagedObjectContext:context];
iosDepartment.departName = @"iOS";
iosDepartment.createDate = [NSDate date];
iosDepartment.employee = zsEmployee;

Department *androidDepartment = [NSEntityDescription insertNewObjectForEntityForName:@"Department" inManagedObjectContext:context];
androidDepartment.departName = @"android";
androidDepartment.createDate = [NSDate date];
androidDepartment.employee = lsEmployee;

// 执行存储操作
NSError *error = nil;
if (context.hasChanges) {
    [context save:&error];
}

// 错误处理
if (error) {
    NSLog(@"Association Table Add Data Error : %@", error);
}

上面创建了四个实体,并且将Employee都关联到Department上,完成关联操作后通过MOC存储到本地。

可以看到上面所有的托管对象创建时,都使用NSEntityDescriptioninsert方法创建,并和上下文建立关系。这时就想问了,我能直接采用传统的init方法创建吗?

会崩的

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值