问题
小明班上最近月考了,老师大明想要给一部分优秀的同学进行奖励,而另外一部分要进行查漏补缺。大明决定将总分排名前10的,各科成绩排名前10的以及排名最后10名的按从高到低的顺序找出来。以前大明都是在家用笔一个个划出来。不过最近大明在长沙戴维营教育接受了残酷的iOS培训,决定装逼一把,给自己的“肾6+”开发了一款应用。只要各科老师将成绩提交给他,就可以直接看到这些学生的成绩了,并且各种曲线、柱状图、饼图。每个学生的情况就好比没穿衣服一样”透明“。现在的问题是,大明并不想自己去实现各种筛选和排序算法。
解决方法
很快大明就想到了戴维营教育的博客上Core Data除了简单的存取功能外,还具备各种取数据的方法。
一、数据获取
Core Data中获取数据必须通过NSFetchRequest进行。我们有两种方式获取NSFetchRequest对象。
通过实体名称创建NSFetchRequest对象。 这种方式其实就是我们在前面两篇文章中用来获取数据的技巧。
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:@"Person"];
//或者
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:@"Person" inManagedObjectContext:context];
fetchRequest.entity = entity;
通过模型文件中创建的请求模版创建。
//使用managedModel获取fetchRequest模版
NSFetchRequest *fetchRequest = [appDelegate.managedObjectModel fetchRequestTemplateForName:@"personFR"];
我们可以指定fetchRequest的结果类型来获取不同数据,如存储的对象、结果数目等。
// NSFetchRequest *fetchRequest = [appDelegate.managedObjectModel fetchRequestTemplateForName:@"personFR"];
//如果需要改变结果的类型,不能使用从模版生成的request对象
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:@"Person"];
//获取结果总数
fetchRequest.resultType = NSCountResultType;
不过我们也不只一种获取结果数目的方式。在Context里面提供了一系列的操作request的方法,其中就包括了获取结果数目的功能。
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:@"Person"];
//获取结果数目
NSUInteger count = [context countForFetchRequest:fetchRequest error:nil];
二、筛选结果集
大明已经可以得到所有学生的成绩信息了,接下来要做的就是对它们进行排序和筛选。
给学生成绩进行排序
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:@"Person"];
//排序描述符,按score降序排列
NSSortDescriptor *sort01 = [NSSortDescriptor sortDescriptorWithKey:@"score" ascending:NO];
//可以同时按多个属性进行排序
fetchRequest.sortDescriptors = @[sort01];
NSArray *result = [context executeFetchRequest:fetchRequest error:nil];
if (result) {
_people = [NSMutableArray arrayWithArray:result];
for (NSObject *obj in _people) {
NSLog(@"%@", [obj valueForKey:@"score"]);
}
}
结果:
2015-02-04 10:54:16.599 02-02-CoreData01[5832:276345] 99
2015-02-04 10:54:16.600 02-02-CoreData01[5832:276345] 60
2015-02-04 10:54:16.600 02-02-CoreData01[5832:276345] 56
2015-02-04 10:54:16.600 02-02-CoreData01[5832:276345] 45
筛选出成绩排名前十的学生
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:@"Person"];
NSSortDescriptor *sort01 = [NSSortDescriptor sortDescriptorWithKey:@"score" ascending:NO];
fetchRequest.sortDescriptors = @[sort01];
//限制只取前十,其实这是有问题的,万一有重复的分数,后面的就取不到了。
fetchRequest.fetchLimit = 10;
NSArray *result = [context executeFetchRequest:fetchRequest error:nil];
使用NSPredicate筛选成绩高于90分的学生
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"score >= 90"];
fetchRequest.predicate = predicate;
进阶
上面的这些数据获取方式都是同步的方式,如果数据量比较大的话,会显著的影响到程序的性能和用户体验。Core Data中也提供了异步数据获取功能。
AppDelegate *appDelegate = (AppDelegate *)[UIApplication sharedApplication].delegate;
NSManagedObjectContext *context = appDelegate.managedObjectContext;
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:@"Person"];
NSSortDescriptor *sort01 = [NSSortDescriptor sortDescriptorWithKey:@"score" ascending:NO];
fetchRequest.sortDescriptors = @[sort01];
fetchRequest.fetchLimit = 2;
//异步请求
NSAsynchronousFetchRequest *asyncRequst = [[NSAsynchronousFetchRequest alloc] initWithFetchRequest:fetchRequest completionBlock:^(NSAsynchronousFetchResult *result) {
for (NSObject *obj in result.finalResult) {
NSLog(@"%@", [obj valueForKey:@"score"]);
}
}];
//执行异步请求
[context executeRequest:asyncRequst error:nil];
注意: 在使用异步请求的时候,需要设置NSManagedContext对象的并发类型,否则会出错。
2015-02-04 12:12:50.709 02-02-CoreData01[6083:300576] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'NSConfinementConcurrencyType context <NSManagedObjectContext: 0x7fb27b72c5f0> cannot support asynchronous fetch request <NSAsynchronousFetchRequest: 0x7fb27b71d750> with fetch request <NSFetchRequest: 0x7fb27b7247a0> (entity: Person; predicate: ((null)); sortDescriptors: ((
"(score, descending, compare:)"
)); limit: 2; type: NSManagedObjectResultType; ).'
解决办法是在创建Context对象的时候,设置它的并发类型。
NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
if (!coordinator) {
return nil;
}
//创建Context对象,并设置并发类型
_managedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
[_managedObjectContext setPersistentStoreCoordinator:coordinator];
参考资料
- Core Data异步操作:http://code.tutsplus.com/tutorials/ios-8-core-data-and-asynchronous-fetching--cms-22241
- Core Data并发操作:http://code.tutsplus.com/tutorials/core-data-from-scratch-concurrency--cms-22131
- 批量更新Core Data:http://code.tutsplus.com/tutorials/ios-8-core-data-and-batch-updates--cms-22164
本文档由长沙戴维营教育整理。