美文网首页iOS Developer
「死磕」Core Data——非标准数据类型的保存

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

作者: AntonyWong | 来源:发表于2017-05-06 23:05 被阅读61次

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

    相关文章

      网友评论

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

        本文链接:https://www.haomeiwen.com/subject/vqiytxtx.html