「死磕」Core Data——非标准数据类型的保存

上一篇写了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 {
    
    idsectionInfo = 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)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的使用、以及非标准数据类型的保存方法。

你可能感兴趣的:(「死磕」Core Data——非标准数据类型的保存)