上一篇写了Core Data的入门,这篇会涉及两部分内容:
其实这个名字,会引起一定的歧义,光看名字,以为是一个普通的视图控制器,其实它并不继承自UIViewController类。
这个类,仅用于高效地管理从Core Data中取回的数据,供UITableView使用,也就是作为UITableView的数据源而存在的。可能是UITableView在iOS开发中太常用了,所以专门造了这个类,和UITableView搭配使用。
需要使用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有多少行数据,利用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中,可以保存数据类型比较有限:
如果是非标准数据类型,如何保存?
UIImage和UIColor这类遵守了NSCoding协议的对象,Core Data会帮你转换为NSData后,保存,取回来,也会帮你从NSData转为相对应的对象。选择Transformable类型即可,
NSArray、NSMutableArray、NSDictionary、NSMutableDictionary也是遵守NSCoding的对象,也可以选择Transformable直接保存。
当然,也可以选择Binary Data:
保存结构体,可以选择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];
两种思路:
@property (nonatomic) UserGenderType userGender;
自定义对象,也有两种思路:
#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保存非标准数据类型,以上都基本涉及了。
以上,就是Core Data中的NSFetchedResultsController的使用、以及非标准数据类型的保存方法。