iOS开发——数据持久化之归档

2016/05/20 iOS开发

在实际的项目开发中,数据持久化是我们必须要考虑的一个事情,如何把我们需要的数据进行持久化处理。

在此之前,一些轻量级的数据类型我一般比较喜欢用NSUserDefaults来存储,因为首先这是一个单例,而且使用简便,数据之间传递自由,所以很适合用来保存简单的数据。

而昨天我在实际工作中,有一个数组NSMutableArray需要存储,但是使用时,报了一个错误 reason: '*** -[NSUserDefaults setObject:forKey:]: attempt to insert non-property list object,这个报错提示我试图加入一个不支持的数据类型。可是明明NSUserDefaults支持的数据类型有:NSNumber(NSInteger、float、double),NSString,NSDate,NSArray,NSDictionary,BOOL,为什么我的NSArray不能加入呢。后来发现我的数组中的对象,是Model类的对象。

既然涉及到了Model类的对象,就要考虑用归档(NSKeyedArchiver)来处理它了。NSKeyedArchiver能够存储的数据范围很广,因为它对应着MVC中的Model层,即实体类。在程序中,我们会在Model层定义很多的entity,例如name、room、device等。

那么对自定义对象的归档显得重要的多,因为很多时候我们都要在程序退出时保存数据,再程序恢复时重新加载,那么,归档便是一个好的选择。

接下来我们来讲讲NSKeyedArchiver的使用。

  • 要使对象可以归档,对象必须实现NSCoding协议,大部分对象都符合NSCoding的协议,一般我们可以在我们的Model类中实现NSCoding协议。

遵循NSCoding协议,我们需要实现两个方法。

  • - (void)encodeWithCoder:(NSCoder *)aCoder;

  • - (nullable instancetype)initWithCoder:(NSCoder *)aDecoder;

  • 同时也建议对象实现NSCoping协议,该协议允许复制对象,遵循该协议要实现- (id)copyWithZone:(nullable NSZone *)zone;方法

下面我们来看归档协议方法的实现

-(void)encodeWithCoder:(NSCoder *)aCoder{
// 这里放置需要持久化的属性
    [aCoder encodeObject:_deviceType forKey:@"deviceType"];
    [aCoder encodeObject:_roomType forKey:@"roomType"];
    [aCoder encodeObject:_brand forKey:@"brand"];
    [aCoder encodeObject:_version forKey:@"version"];
}

接下来是解档的协议方法实现

- (nullable instancetype)initWithCoder:(NSCoder *)aDecoder{
//  这里务必和encodeWithCoder方法里面的内容一致,不然会读不到数据
    if (self = [super init]) {
        self.deviceType = [aDecoder decodeObjectForKey:@"deviceType"];
        self.roomType = [aDecoder decodeObjectForKey:@"roomType"];
        self.brand = [aDecoder decodeObjectForKey:@"brand"];
        self.version = [aDecoder decodeObjectForKey:@"version"];
    }
    return self;
}

接下来是遵循了NSCopying协议的方法实现

#pragma mark NSCoping
- (id)copyWithZone:(NSZone *)zone {
    AddDeviceModel *copy = [[[self class] allocWithZone:zone] init];
    copy.deviceType = [self.deviceType copyWithZone:zone];
    copy.roomType = [self.roomType copyWithZone:zone];
    copy.brand = [self.brand copyWithZone:zone];
    copy.version = [self.version copyWithZone:zone];
    return copy;
}
  • 特别注意 如果需要归档的类是某个自定义类的子类时,就需要在归档和解档之前先实现父类的归档和解档方法。即 [super encodeWithCoder:aCoder][super initWithCoder:aDecoder] 方法

使用

因为之前我提过 我们要存储一个数组,那么我们可以把数组中的数据转化成NSData类型来存储,使用+ (NSData *)archivedDataWithRootObject:(id)rootObject;方法。即 :

        NSData *data = [NSKeyedArchiver archivedDataWithRootObject:_dataSource];
        [[NSUserDefaults standardUserDefaults] setObject:data forKey:kEditInfraredRepeater];
        [[NSUserDefaults standardUserDefaults]synchronize];

三行代码,就把NSKeyedArchiver和NSUserDefaults结合来存储数据了。

而要解档使用数据,只要使用解档NSKeyedUnarchiver类中的+ (nullable id)unarchiveObjectWithData:(NSData *)data;方法就可以实现

        NSData *data = [[NSUserDefaults standardUserDefaults] objectForKey:kEditInfraredRepeater];
        NSArray *array = [NSKeyedUnarchiver unarchiveObjectWithData:data];
        _dataSource = [NSMutableArray arrayWithArray:array];

同样也是三行代码实现。

以上就是最简单的归档解档数据持久化的实现方式,至于如何用runtime进行自动归档解档,就又需要日后深入研究了。

Search

    Table of Contents