上一篇写了Core Data的入门,这篇会涉及两部分内容:
- NSFetchedResultsController的使用。
- 非标准数据类型的保存。
NSFetchedResultsController的使用
其实这个名字,会引起一定的歧义,光看名字,以为是一个普通的视图控制器,其实它并不继承自UIViewController类。
这个类,仅用于高效地管理从Core Data中取回的数据,供UITableView使用,也就是作为UITableView的数据源而存在的。可能是UITableView在iOS开发中太常用了,所以专门造了这个类,和UITableView搭配使用。
创建一个NSFetchedResultsController
需要使用NSFetchedResultsController,首先初始化,创建一个NSFetchedResultsController对象:
(在这段代码之前,我们已经声明了一个属性@property (strong, nonatomic) NSFetchedResultsController *fetchedResultsController;
)
- (void)initializeFetchedResultsController {
// 这里的kUserEntityName就是你在xcdatamodeld文件的实体名称。
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:kUserEntityName];
// 返回数据的排序规则(最少需要有一个sort descriptor)
// Need at least one sort descriptor
NSSortDescriptor *nameSort = [NSSortDescriptor sortDescriptorWithKey:kUserNameKey ascending:YES];
[request setSortDescriptors:@[nameSort]];
// 实例化fetchedResultsController对象
// 需要利用在此之前已经创建的NSManagedObjectContext对象
// 最后一个参数,可以复制一个字符串,Core Data会自己设置缓存,以提升性能。
[self setFetchedResultsController:[[NSFetchedResultsController alloc] initWithFetchRequest:request managedObjectContext:[SPKManager shareManager].store.context sectionNameKeyPath:nil cacheName:nil]];
// 设置委托对象
// 协议中有4个委托方法,用来告诉UITableView,Core Data中的数据有变化
[self.fetchedResultsController setDelegate:self];
NSError *error = nil;
if (![[self fetchedResultsController] performFetch:&error]) {
NSLog(@"Failed to initialize FetchedResultsController: %@\n%@", [error localizedDescription], [error userInfo]);
abort();
}
}
以上的初始化方法,会放在UITableViewController类中进行。
为Table View提供数据源
要告诉Table View有多少行数据,利用NSFetchedResultsController的sections属性。
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
id<NSFetchedResultsSectionInfo>sectionInfo = self.fetchedResultsController.sections[section];
return [sectionInfo numberOfObjects];
}
要拿回具体的某个对象,利用NSFetchedResultsController的objectAtIndexPath:方法:
SPKUser *user = [self.fetchedResultsController objectAtIndexPath:indexPath];
cell.textLabel.text = user.userName;
cell.detailTextLabel.text = [NSString stringWithFormat:@"%@", @(user.userID)];
// 如果没有自定义NSManagedObject子类,就应该类似:NSManagedObject *object = [self.fetchedResultsController objectAtIndexPath:indexPath];
监视数据的变化
当Core Data中的数据发生变化时,可以通过 NSFetchedResultsControllerDelegate中的委托方法,方便监视数据的变化,自动更新UI。
实现协议的四个方法
#pragma mark - NSFetchedResultsControllerDelegate
- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller{
[_tableView beginUpdates];
}
- (void)controller:(NSFetchedResultsController *)controller didChangeSection:(id<NSFetchedResultsSectionInfo>)sectionInfo atIndex:(NSUInteger)sectionIndex forChangeType:(NSFetchedResultsChangeType)type {
switch (type) {
case NSFetchedResultsChangeInsert:
// 插入了section
[_tableView insertSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeDelete:
// 删除了section
[_tableView deleteSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeMove:
break;
case NSFetchedResultsChangeUpdate:
break;
default:
break;
}
}
- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type newIndexPath:(NSIndexPath *)newIndexPath {
switch (type) {
case NSFetchedResultsChangeInsert:
// 插入了新对象
[_tableView insertRowsAtIndexPaths:@[newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeDelete:
// 删除了对象
[_tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeUpdate:
// 修改了对象
[self configureCell:[_tableView cellForRowAtIndexPath:indexPath] atIndexPath:indexPath];
break;
case NSFetchedResultsChangeMove:
// 移动了对象
[_tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade];
[_tableView insertRowsAtIndexPaths:@[newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
break;
default:
break;
}
}
- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller {
[_tableView endUpdates];
}
非标准数据类型的处理
在Core Data中,可以保存数据类型比较有限:
- Integer 16
- Integer 32
- Integer 64
- Decimal (高精度大数,不会四舍五入,适用于金融领域)
- Double
- Float
- String
- Boolean
- Date
- Binary Data
- Transformable
如果是非标准数据类型,如何保存?
UIImage、UIColor
UIImage和UIColor这类遵守了NSCoding协议的对象,Core Data会帮你转换为NSData后,保存,取回来,也会帮你从NSData转为相对应的对象。选择Transformable类型即可,
数组,字典
NSArray、NSMutableArray、NSDictionary、NSMutableDictionary也是遵守NSCoding的对象,也可以选择Transformable直接保存。
当然,也可以选择Binary Data:
- 保存前,调用NSKeyedUnarchiver的archivedDataWithRootObject:方法返回NSData类型数据,让Core Data可以对其进行保存;
- 取回时,用NSKeyedUnarchiver的unarchiveObjectWithData:方法,将取回的NSData数据,转换回数组、字典对象。
结构体
保存结构体,可以选择Transformable类型。
然后在声明属性类型的时候,使用NSValue类型,如@property (nullable, nonatomic, retain) NSValue *imgeRect;
赋值时,进行转化,如下:
newUser.imgeRect = [NSValue valueWithCGRect:CGRectMake(0.0, 0.0, 100.0, 100.0)];
获取值的时候,再进行转换,如下:
CGRect imageRect = [firstUser.imgeRect CGRectValue];
枚举类型
两种思路:
-
选择Integer 16,当作一个整数机型保存。(声明的时候,就可以用枚举类型了)。如
@property (nonatomic) UserGenderType userGender;
-
选择Transformable,然后重写get、set方法,进行转换……还是用上面的方法吧,比较简单:)
自定义对象
自定义对象,也有两种思路:
- 直接定义成xcdatamodeld文件中的一个实体,作为NSManagedObject类的子类,由CoreData直接保存;
- 如果不定义成实体,需要:
- 该自定义对象需要遵守NSCoding协议并实现required方法(initWithCoder:方法和encodeWithCoder:方法);
- xcdatamodeld文件中该特性数据类型选择为Transformable
- 创建一个NSValueTransformer子类,重写transformedValue:和reverseTransformedValue:方法,手动进行数据类型的转换(本质就是自定义对象和NSData互转)类似如下:
#import "HAEqTransformer.h"
#import "HAEq.h"
// 我的自定义对象是HAEq
@implementation HAEqTransformer
+ (Class)transformedValueClass {
return [NSData class];
}
- (id)transformedValue:(id)value {
if (!value) {
return nil;
}
if ([value isKindOfClass:[NSData class]]) {
return value;
}
HAEq *eq = (HAEq *)value;
// 将自定义对象转换成NSData
NSData *dataFromEq = [NSKeyedArchiver archivedDataWithRootObject:eq];
return dataFromEq;
}
- (id)reverseTransformedValue:(id)value {
NSData *data = (NSData *)value;
// 将NSData对象转换为自定义对象
HAEq *eq = [NSKeyedUnarchiver unarchiveObjectWithData:data];
return eq;
}
@end
所以,利用Core Data保存非标准数据类型,以上都基本涉及了。
End
以上,就是Core Data中的NSFetchedResultsController的使用、以及非标准数据类型的保存方法。
网友评论