三月 30, 2018

iOS中使用NSCoder来存储数据

iOS中数据的存储(可持久化的存储)有如下几种方式:

  1. NSUserDefaults.这种方式基本也是用的最多的方式,可以直接以key-value的形式保存数据。
  2. 文件存储。直接将数据以文件的形式存储。
  3. Keychain。这种存储一般用来保存一些秘钥类的数据,支持跨app使用
  4. 数据库。一般的话就是sqllite、CoreData

本篇博客的目的并不是介绍以上几种数据存储方式的使用方法,因为如何使用,网上一大把,没必要再重复单独写一篇博客介绍。

本次主要针对NSUserDefaults的几个问题提出一个改良的数据存储方案。

虽然NSUserDefaults支持key-value形式存储数据,但是仅此而已了。在项目中我们可以直接使用NSUserDefaults的单例存储数据,虽然NSUserDefaults可以通过其实方式存储数据,但是在平常项目中用的最多的还是直接通过standardUserDefaults来获取单例存储数据,因此对于项目中往NSUserDefaults到底保存了多少数据没有一个明确直观的展示的(当然我们可以通过dictionaryRepresentation来获取所有保存的keys),也不能直接保存对象,很难统计到一个key到底在多少个地方被使用了,因此我们需要一个即直观又能保存不同对象的数据存储方案。

首先是数据的直观性,显然将数据直接以一个对象的属性来保存的话是最直观的,你可以对属性添加注释、设置数据类型、名称等,甚至对属性的设置添加各种自定义方法。

然后是如果保存对象,NSUserDefaults本身不支持直接保存对象,想要保存对象,那么必须先将对象序列化。我们可以采用JSON、NSCoder、XML等方式来将对象序列化。不同的序列化方式的成本也是不同的,最简单的还是直接使用NSCoder的方式来序列化,但是如果我们需要保存的数据类型很复杂,并且可能带有对象嵌套对象的情况,那么采用JSON的序列化方式显然是更加合理的方式。什么?你说iOS中JSON不支持对象套对象甚至是Array的反序列化?其实是可以的,只需要借助iOS中提供的反射库+几个小技巧就能实现,这个我准备在下一篇文章中详细介绍下。XML?直接忽略吧!基本没人用了

其实要是项目中使用了protocolbuffer库的话,采用PB的形式来序列化、反序列化是最好的方式了。其实我个人强烈推荐采用PB的方式来实现数据的序列化。为什么?因为PB好用啊,最重要的是我们可以偷懒啊。

那么这样一来,实际的存储方案如下:
1. 创建一个自定义的数据保存类。在里面定义各种我们需要保存的数据字段,并且给每个字段加上注释,以便项目中的其他同事可以一目了然,一看就知道你保存的是什么。
2. 添加一个save方法,在里面实现将对象序列化为NSData或者NSString的方法,如果需要保存的数据涉及到对象嵌套对象的话,那么序列化的时候务必也要将涉及到的所有对象都序列化。然后将序列化后得到的NSData或者NSString保存到NSUserDefaults里面。
3. 添加一个反序列的方法,用来将数据反序列化成我们定义的对象。

下面提供一段代码来演示下。例子中是采用NSCode来实现的

#import "LocalStorage.h"

@implementation LocalStorage
+(instancetype)instance{
    static LocalStorage *_instance;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        NSData  *data =[[NSUserDefaults standardUserDefaults] valueForKey:@"LocalStorageData"];
        if(data){
            _instance = [NSKeyedUnarchiver unarchiveObjectWithData:data];
        }else{
            _instance = [LocalStorage new];
        }
    });
    return _instance;
}

- (void)encodeWithCoder:(NSCoder *)aCoder{
    // 序列化数据
    [aCoder encodeObject:@(self.number) forKey:@"number"];
}

- (id)initWithCoder:(NSCoder *)aDecoder{
    if (self=[super init])
    {
        // 反序列化
        self.number =[[aDecoder decodeObjectForKey:@"number"] integerValue];
    }
    return (self);
}

-(void)save{
    dispatch_async(dispatch_get_main_queue(), ^{
        NSData  *data = [NSKeyedArchiver archivedDataWithRootObject:self];
        [[NSUserDefaults standardUserDefaults] setObject:data forKey:@"LocalStorageData"];
        [[NSUserDefaults standardUserDefaults] synchronize];
    });
}
@end

发表评论

电子邮件地址不会被公开。 必填项已用*标注